题面:
题意:
给定点 a ( x , y , z ) , 问符合要求的点 b( xx , yy , zz)的dp最大值,设这个dp最大值为dp [ x ],那么当前点 a的dp [ a ] = dp [ x ] + 1。
若没有符合要求的点,那么 dp [ a ] = 0。
其中点b符合要求是指 xx<=x && yy<=y && zz<=z 且 (xx < x , yy < y , zz<z)之中至少有一个成立。
这里的点由于是随机生成且随机数较大 点的数量比较少,我们可以认为没有重复的点,那么可以认为 xx<=x && yy<=y && zz<=z 即可。
实际上,若有重复的点,那么对于重复的点,符合要求的点的答案一定是一样的,对于某个重复k次的点,我们只查询一次作为这k的点的答案,插入时插入k次。
题解:写的时候是用线段树+平衡树写的。
sort 一维 x
线段树一维 y
平衡树 一维同时维护dp [ x ] 的最大值
时间复杂度 O(n logn logn)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#define ll long long
#define llu unsigned ll
#define pr make_pair
#define pb push_back
#define llc (cnt<<1)
#define rrc (cnt<<1|1)
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=100100;
unsigned long long k1, k2;
int n;
unsigned long long CoronavirusBeats()
{
unsigned long long k3 = k1, k4 = k2;
k1 = k4;
k3 ^= k3 << 23;
k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
return k2 + k4;
}
//平衡树
struct nn
{
int val;
int lc,rc;
int si,cm,mm;
}t[maxn*22];
int tot,root;
struct node
{
llu x,y,z;
int id;
}a[maxn];
void pushup(int p)
{
t[p].si=t[t[p].lc].si+t[t[p].rc].si+1;
t[p].mm=max(t[p].cm,max(t[t[p].lc].mm,t[t[p].rc].mm));
}
int dp[maxn],maxx=0;
int ans[maxn];
int newnode(int x)
{
int p=++tot;
t[p].lc=t[p].rc=0;
t[p].si=1;
t[p].val=x;
t[p].cm=t[p].mm=dp[x];
return p;
}
void R(int &p)
{
int q=t[p].lc;
t[p].lc=t[q].rc;
t[q].rc=p;
p=q;
pushup(t[p].rc);
pushup(p);
}
void L(int &p)
{
int q=t[p].rc;
t[p].rc=t[q].lc;
t[q].lc=p;
p=q;
pushup(t[p].lc);
pushup(p);
}
void RL(int &p)
{
R(t[p].rc);
L(p);
}
void LR(int &p)
{
L(t[p].lc);
R(p);
}
void maintain(int &p,bool flag)
{
if(!flag)
{
if(t[t[t[p].lc].lc].si>t[t[p].rc].si)
R(p);
else if(t[t[t[p].lc].rc].si>t[t[p].rc].si)
LR(p);
else return ;
}
else
{
if(t[t[t[p].rc].rc].si>t[t[p].lc].si)
L(p);
else if(t[t[t[p].rc].lc].si>t[t[p].lc].si)
RL(p);
else return ;
}
maintain(t[p].lc,false);
maintain(t[p].rc,true);
maintain(p,true);
maintain(p,false);
}
void _insert(int &now,int val)
{
if(!now)
{
now=newnode(val);
return ;
}
if(a[val].z<a[t[now].val].z)
{
_insert(t[now].lc,val);
maintain(now,false);
}
else
{
_insert(t[now].rc,val);
maintain(now,true);
}
pushup(now);
}
int get_front(int x,int root)
{
int now=root,ans=-1;
while(now)
{
if(a[t[now].val].z<=a[x].z) ans=max(ans,max(t[now].cm,t[t[now].lc].mm)),now=t[now].rc;
else
now=t[now].lc;
}
return ans;
}
//线段树
struct tree
{
int l,r;
int rt;
}tt[maxn<<2];
llu b[maxn];
bool cmp(const node &a,const node &b)
{
if(a.x!=b.x) return a.x<b.x;
else if(a.y!=b.y) return a.y<b.y;
else return a.z<b.z;
}
void build(int l,int r,int cnt)
{
tt[cnt].l=l,tt[cnt].r=r;
tt[cnt].rt=0;
if(l==r) return ;
int mid=(l+r)>>1;
build(l,mid,llc);
build(mid+1,r,rrc);
}
void init(int n)
{
for (int i = 1; i <= n; ++i)
{
a[i].x = CoronavirusBeats();
a[i].y = CoronavirusBeats();
a[i].z = CoronavirusBeats();
a[i].id=i;
b[i]=a[i].y;
}
sort(b+1,b+n+1);
int cp=unique(b+1,b+n+1)-(b+1);
for(int i=1;i<=n;i++)
{
a[i].y=(llu)(lower_bound(b+1,b+cp+1,a[i].y)-b);
}
build(1,cp,1);
}
void change(int pos,int cnt)
{
_insert(tt[cnt].rt,pos);
if(tt[cnt].l==tt[cnt].r) return ;
if(a[pos].y<=tt[llc].r) change(pos,llc);
else change(pos,rrc);
}
void ask(int l,int r,int cnt,int id)
{
if(l>r) return ;
if(tt[cnt].rt==0) return ;
if(l<=tt[cnt].l&&tt[cnt].r<=r)
{
dp[id]=max(dp[id],get_front(id,tt[cnt].rt)+1);
return ;
}
if(l<=tt[llc].r) ask(l,r,llc,id);
if(r>=tt[rrc].l) ask(l,r,rrc,id);
}
int main(void)
{
scanf("%d%llu%llu",&n,&k1,&k2);
init(n);
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
ask(1,a[i].y,1,i);
change(i,1);
}
for(int i=1;i<=n;i++)
ans[a[i].id]=dp[i],maxx=max(maxx,dp[i]);
printf("%d\n",maxx+1);
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
return 0;
}
这个三维偏序好像不能用CDQ分治,因为假设当前处理完了区间 [ l , mid ] , [ mid+1 , r ]
我们现在要合并区间 [ l , mid ] , [ mid+1 , r ]。
现在有一点 x ∈ [ mid+1 , r ] ,区间 [ l , mid ] 对 x 点的总贡献并不好求,单单是区间[ l , mid ] 对x 点的贡献是比较好好求的,一般的三维偏序处理即可。但是 [ l , mid ] 可能会对 xx 点有贡献,进而影响 x 的值。