一般的最长上升子序列是二维偏序,所以排序后用线段树维护可以达到nlogn程度,但是如果是三元组呢?
因为多了一个偏序关系,所以维护起来相当麻烦。
roosephu说了一句经典的:一维快排,二维归并,三维树状数组,就是一维排序x后,再而在快排保证前一段x小于后一段x基础上归并排序做y,同时此基础上树状数组维护z。(详见http://blog.csdn.net/cjoilmd/article/details/6884432,速度完虐std)
但是,如果用树套树就没这么麻烦,一维排序后,二三维线段树套平衡树。
事实证明在roosephu精心随机的数据下,裸bst比treap要快一半以上。
(被隐掉的是treap旋转等操作)
{$inline on} uses math; type rex=record x,y,z:longint end; var rt,l,r,d,id,key,maxk:array[1..1048576]of longint; ans,ss,n,m,m1:longint; f:array[1..500000]of longint; a:array[1..500000]of rex; procedure inf; begin assign(input,'godfarmer.in');reset(input); assign(output,'godfarmer.out');rewrite(output) end; procedure ouf; begin close(input);close(output) end; {procedure left(x:longint);inline; var y,z:longint; begin y:=rt[x];z:=rt[y]; if l[z]=y then l[z]:=x else r[z]:=x;rt[x]:=z; r[y]:=l[x];rt[l[x]]:=y; l[x]:=y;rt[y]:=x; maxk[y]:=max(maxk[l[y]],max(maxk[r[y]],key[y])); maxk[x]:=max(maxk[l[x]],max(maxk[r[x]],key[x])); maxk[z]:=max(maxk[l[z]],max(maxk[r[z]],key[z])) end; procedure right(x:longint);inline; var y,z:longint; begin y:=rt[x];z:=rt[y]; if l[z]=y then l[z]:=x else r[z]:=x;rt[x]:=z; l[y]:=r[x];rt[r[x]]:=y; r[x]:=y;rt[y]:=x; maxk[y]:=max(maxk[l[y]],max(maxk[r[y]],key[y])); maxk[x]:=max(maxk[l[x]],max(maxk[r[x]],key[x])); maxk[z]:=max(maxk[l[z]],max(maxk[r[z]],key[z])) end;} function ori(i,x,w:longint ): longint;inline; begin inc(ss);rt[ss]:=i;d[ss]:=x;maxk[ss]:=w;key[ss]:=w; // id[ss]:=random(maxlongint); exit(ss) end; procedure ins(s,x,w:longint);inline; var i:longint; begin i:=s; while true do begin if w>maxk[i] then maxk[i]:=w; if x>d[i] then begin if r[i]=0 then begin r[i]:=ori(i,x,w); i:=r[i]; break end else i:=r[i] end else begin if l[i]=0 then begin l[i]:=ori(i,x,w); i:=l[i]; break end else i:=l[i] end end; { while id[i]<id[rt[i]] do if l[rt[i]]=i then right(i) else left(i);} end; procedure change(x,w:longint);inline; var y:longint; begin y:=x;x:=a[x].y+m1; while x<>0 do begin ins(x,a[y].z,w); x:=x>>1 end; end; function search(s,w:longint ): longint;inline; var i,maxf:longint; begin i:=r[s]; maxf:=0; while i<>0 do begin if d[i]<w then begin maxf:=max(maxf,max(key[i],maxk[l[i]])); i:=r[i] end else if d[i]>w then begin i:=l[i] end else begin maxf:=max(maxf,max(key[i],maxk[l[i]])); break end end; exit(maxf) end; function find(l,r,i:longint ): longint;inline; var cos:longint; begin l:=l+m1-1;r:=r+m1+1;find:=0; while not(l xor r=1) do begin if l and 1=0 then begin cos:=search(l+1,a[i].z); if cos>find then find:=cos end; if r and 1=1 then begin cos:=search(r-1,a[i].z); if cos>find then find:=cos end; l:=l>>1;r:=r>>1 end; end; procedure origin; inline; var i:longint; begin m1:=1; while m1<=n+2 do m1:=m1<<1; for i:=1 to n+m1 do begin id[i]:=-maxlongint; d[i]:=-maxlongint; rt[i]:=i end; ss:=n+m1 end; procedure qsort(l,r:longint);inline; var i,j,x:longint; c:rex; begin i:=l;j:=r;x:=a[(l+r)>>1].x; repeat while a[i].x<x do inc(i); while x<a[j].x do dec(j); if not(i>j) then begin c:=a[i];a[i]:=a[j];a[j]:=c; inc(i);dec(j) end until i>j; if i<r then qsort(i,r); if l<j then qsort(l,j) end; procedure init; var i,x:longint; begin readln(n); origin; for i:=1 to n do begin read(x);a[x].x:=i end; for i:=1 to n do begin read(x);a[x].y:=i end; for i:=1 to n do begin read(x);a[x].z:=i end; qsort(1,n); origin; for i:=1 to n do begin f[i]:=find(1,a[i].y-1,i)+1; if i<>n then change(i,f[i]); end; ans:=0; for i:=1 to n do if f[i]>ans then ans:=f[i]; writeln(ans) end; begin inf; randomize; init; ouf end.
update:14.6.25
cdq分治
第一维先排序,处理第二维的时候,对于每个区间[l,r],要保证在处理的时候[1,l-1]的影响都已经处理过了,然后先递归处理[l,mid],因为[mid+1,r]不会产生影响,之后通过第二维的顺序依次处理[l,mid]对[mid+1,r]的影响,第三维在按第二维枚举的时候用数据结构维护,然后再递归处理[mid+1,r].
如果不想枚举第二维的时候快排,可以用归并来做,那么每次处理完[l,r]之后要按第二维再归并成一个有序序列
#include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int Max[200000],u[200000],p[20][200000],f[200000]; int n,a[200000][3],t[200000],w_time; void change(int x,int w) { for (;x<=n;x+=(x & -x)) if (t[x]!=w_time) { t[x]=w_time; Max[x]=w; } else Max[x]=max(Max[x],w); } int ask(int x) { int sum=0; for (;x;x-=(x & -x)) if (t[x]==w_time) sum=max(sum,Max[x]); return sum; } bool fl[200000]; bool cmp1(int i,int j) { return a[i][1]<a[j][1]; } void calc(int e,int l,int r) { if (l==r) return ; int mid=(l+r)>>1; calc(e+1,l,mid); w_time++; for (int i=l;i<=r;i++) p[e][i]=u[i],fl[u[i]]=(i<=mid); sort(p[e]+l,p[e]+r+1,cmp1); for (int i=l;i<=r;i++) if (fl[p[e][i]]) change(a[p[e][i]][2],f[p[e][i]]); else f[p[e][i]]=max(f[p[e][i]],ask(a[p[e][i]][2])+1); calc(e+1,mid+1,r); //for (int i=l;i<=r;i++) u[i]=p[e][i]; } bool cmp(int i,int j) { return a[i][0]<a[j][0]; } int main() { freopen("godfarmer.in","r",stdin); freopen("godfarmer.out","w",stdout); scanf("%d",&n); for (int e=0;e<=2;e++) for (int i=1;i<=n;i++) { int x; scanf("%d",&x); a[x][e]=i; } for (int i=1;i<=n;i++) u[i]=i; sort(u+1,u+n+1,cmp); for (int i=1;i<=n;i++) f[i]=1; calc(0,1,n); int ans=0; for (int i=1;i<=n;i++) ans=max(ans,f[i]); // for (int i=1;i<=n;i++) cout<<f[i]<<' ';cout<<endl; printf("%d\n",ans); return 0; }