三维偏序 树套树&cdq分治

一般的最长上升子序列是二维偏序,所以排序后用线段树维护可以达到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;
} 


你可能感兴趣的:(function,search,Random,input,output)