LG3769 TATT
TATT
四维空间真是美妙。 现在有\(n\)个四维空间中的点,请求出一条最长的路径,满足任意一维坐标都是单调不降的。 注意路径起点是任意选择的,并且路径与输入顺序无关(路径顺序不一定要满足在输入中是升序)。
路径的长度是经过的点的数量,任意点只能经过一次。
\(n\leq 5\times 10^4\)。
题解
https://www.luogu.com.cn/blog/hs-black/solution-p3769
这题是四维意义下的最长上升子序列, 但如果将第一维排序就变成三维问题了, kd-tree时间复杂度应该会更优
总而言之就是
\[dp_i = \max_{x_j\le x_i,y_j\le y_i,z_j \le z_i} dp_j + 1
\]
从前向后扫, 找到三维都比它小的dp值最大的点, 找点可以用kd-tree来优化
可以采用以下技巧和剪枝:
-
如果当前子树dp最大值小于等于当前已搜出的最优答案直接返回即可肯定不会更新了
-
如果当前子树的点都在范围内, 直接拿最大值更新答案返回
-
如果当前子树有一维的最小值超过了范围, 那么代表着这个子树中无一满足条件, 直接返回即可
-
每次插入不用替罪羊思想重构, 可以直接提前把树建出来, 初始dp值都设为零, 加入操作相当于激活一个点, 具体就是像线段树那样从上向下搜到那个点, 返回时一路更新就行了
有了以上剪枝, 足够通过此题
CO int N=5e4+10;
array<int,4> p[N];
int q[N],ch[N][2];
array<int,3> mx[N],mn[N];
IN void push_up(int x){
for(int i=0;i<=2;++i){
mx[x][i]=mn[x][i]=p[x][i+1];
if(ch[x][0]) mx[x][i]=max(mx[x][i],mx[ch[x][0]][i]),mn[x][i]=min(mn[x][i],mn[ch[x][0]][i]);
if(ch[x][1]) mx[x][i]=max(mx[x][i],mx[ch[x][1]][i]),mn[x][i]=min(mn[x][i],mn[ch[x][1]][i]);
}
}
int build(int l,int r,int i){
if(l>r) return 0;
int mid=(l+r)>>1;
nth_element(q+l,q+mid,q+r+1,[&](int a,int b)->bool{
return p[a][i]<p[b][i];
});
int x=q[mid];
ch[x][0]=build(l,mid-1,i%3+1);
ch[x][1]=build(mid+1,r,i%3+1);
push_up(x);
return x;
}
int f[N],g[N],tmp;
IN bool in(int a[],int b[]){
for(int i=0;i<=2;++i)if(a[i]>b[i]) return 0;
return 1;
}
void query(int x,int y){
if(g[x]<=tmp) return;
if(!in(mn[x].data(),p[y].data()+1)) return;
if(in(mx[x].data(),p[y].data()+1)) {tmp=g[x]; return;}
if(in(p[x].data()+1,p[y].data()+1)) tmp=max(tmp,f[x]);
if(ch[x][0]) query(ch[x][0],y);
if(ch[x][1]) query(ch[x][1],y);
}
void insert(int x,int y){
if(x==y) {f[x]=tmp,g[x]=max(g[x],f[x]); return;}
if(!in(p[y].data()+1,mx[x].data()) or !in(mn[x].data(),p[y].data()+1)) return;
if(ch[x][0]) insert(ch[x][0],y),g[x]=max(g[x],g[ch[x][0]]);
if(ch[x][1]) insert(ch[x][1],y),g[x]=max(g[x],g[ch[x][1]]);;
}
int main(){
int n=read<int>();
for(int i=1;i<=n;++i)
for(int j=0;j<=3;++j) read(p[i][j]);
sort(p+1,p+n+1,[&](CO array<int,4>&a,CO array<int,4>&b)->bool{
for(int i=0;i<=3;++i)
if(a[i]!=b[i]) return a[i]<b[i];
return 0;
});
iota(q+1,q+n+1,1);
int root=build(1,n,1);
int ans=0;
for(int i=1;i<=n;++i){
tmp=0,query(root,i),++tmp;
insert(root,i);
ans=max(ans,tmp);
}
printf("%d\n",ans);
return 0;
}
静渊以有谋,疏通而知事。