Description
小C正在出一道题...因为语文水平有限他想不出复杂的背景,所以以下就是题意了。
平面上有N个点,开始时每个点属于一个不同的集合。不妨设点Pi属于集合Si。请维护数据结构支持以下三种操作:
"Merge x y":将集合Sy中的点到Sx中;
"Split i d v"(d ∈ {0, 1}):创建新集合Sc + 1,Sc + 2,之后将集合Si中的所有点中,第d维坐标不超过v的到集合Sc + 1中,超过v的移动到集合Sc + 2中,其中c为现有集合数( 包括之前合并和分割中产生的空集 );
"Query i":查询集合Si中点权值的最大值,最小值及和;
"Add i d":将集合Si中所有点的权值增加d。
方便起见,对于d ∈ {0, 1},输入的所有点的第d维坐标不重复。所有操作涉及的集合非空。
平面上有N个点,开始时每个点属于一个不同的集合。不妨设点Pi属于集合Si。请维护数据结构支持以下三种操作:
"Merge x y":将集合Sy中的点到Sx中;
"Split i d v"(d ∈ {0, 1}):创建新集合Sc + 1,Sc + 2,之后将集合Si中的所有点中,第d维坐标不超过v的到集合Sc + 1中,超过v的移动到集合Sc + 2中,其中c为现有集合数( 包括之前合并和分割中产生的空集 );
"Query i":查询集合Si中点权值的最大值,最小值及和;
"Add i d":将集合Si中所有点的权值增加d。
方便起见,对于d ∈ {0, 1},输入的所有点的第d维坐标不重复。所有操作涉及的集合非空。
Input
第一行一个整数N表示点数。
接下来N行,每行3个整数分别表示Pi的坐标与权值。
接下来一个整数Q表示操作数。
接下来Q行,每行描述一个操作,格式如上所述。
接下来N行,每行3个整数分别表示Pi的坐标与权值。
接下来一个整数Q表示操作数。
接下来Q行,每行描述一个操作,格式如上所述。
Output
对于每个Q操作,输出三个整数,依次表示询问集合权值的最大值,最小值与和。
预处理出点集的一颗二维k-d tree
将每个点到根的路径复制出来作为初始状态
由于树形态相同,合并和分裂可以仿照线段树
分裂时增加的点数$T(n)=2T(\frac{n}{4})+O(1)=O(\sqrt{n})$
合并复杂度与减少的点数同阶
因此总时间复杂度$O(q\sqrt{n})$
由每个点到根的路径长度之和可得空间复杂度的上界$O(nlogn)$
#include<bits/stdc++.h> #define G getchar() int _(){ int x=0,f=1,c=G; while(c<48)c=='-'&&(f=-1),c=G; while(c>47)x=x*10+c-48,c=G; return x*f; } int _s(){ int c=G,c0; while(c<33)c=G; c0=c; while(c>32)c=G; return c0; } int min(int a,int b){return a<b?a:b;} int max(int a,int b){return a>b?a:b;} const int N=100007,inf=0x3f3f3f3f; int n,qp; struct node; node*newnode(); struct node{ node*c[2]; int mn[2],mx[2]; int mnv,mxv,sz,id; long long sum,a; void add(long long x){ if(this){ a+=x; mnv+=x; mxv+=x; sum+=x*sz; } } void dn(){ if(a){ c[0]->add(a); c[1]->add(a); a=0; } } void up(){ mnv=inf;mxv=-inf;sum=0; sz=0; if(c[0]){ mnv=c[0]->mnv; mxv=c[0]->mxv; sum=c[0]->sum; sz=c[0]->sz; } if(c[1]){ mnv=min(mnv,c[1]->mnv); mxv=max(mxv,c[1]->mxv); sum+=c[1]->sum; sz+=c[1]->sz; } } void set(int x0,int x1,int v){ mn[0]=mx[0]=x0; mn[1]=mx[1]=x1; sum=mnv=mxv=v; sz=1;a=0; } void query(){ printf("%d %d %lld\n",mxv,mnv,sum); } void upds(){ for(int i=0;i<2;++i){ mn[i]=min(c[0]->mn[i],c[1]->mn[i]); mx[i]=max(c[0]->mx[i],c[1]->mx[i]); } } }mem[N*30],*pool[N*30],*rt0,*n0[N],*rts[N*3]; int pp; node*newnode(){ return pool[--pp]; } int dx=0; bool cmp(node*a,node*b){ return a->mn[dx]<b->mn[dx]; } node*build(int L,int R){ if(L==R)return n0[L]; int M=(L+R)>>1; std::nth_element(n0+L,n0+M,n0+R+1,cmp); node*w=newnode(); dx^=1; w->c[0]=build(L,M); w->c[1]=build(M+1,R); w->upds(); dx^=1; return w; } node*ss[50]; int sp=0; void init(node*w){ ss[sp++]=w; if(w->id){ node*t=w; for(int i=sp-2;~i;--i){ node*u=newnode(); *u=*ss[i]; int d=u->c[1]==ss[i+1]; u->c[d]=t; u->c[d^1]=0; u->up(); t=u; } rts[w->id]=t; }else{ init(w->c[0]); init(w->c[1]); } --sp; } int _x,_d; void split(node*w,node*&l,node*&r){ if(!w)l=r=0; else if(_x<w->mn[_d])l=0,r=w; else if(w->mx[_d]<=_x)l=w,r=0; else{ w->dn(); node*u=newnode(); *u=*w; split(w->c[0],w->c[0],u->c[0]); split(w->c[1],w->c[1],u->c[1]); w->up();u->up(); l=w,r=u; } } node*merge(node*a,node*b){ if(!a)return b; if(!b)return a; a->dn();b->dn(); a->c[0]=merge(a->c[0],b->c[0]); a->c[1]=merge(a->c[1],b->c[1]); a->up(); pool[pp++]=b; return a; } int main(){ n=_(); pp=n*30; for(int i=0;i<pp;++i)pool[i]=mem+i; for(int i=1,x,y;i<=n;++i){ x=_();y=_(); n0[i]=newnode(); n0[i]->set(x,y,_()); n0[i]->id=i; } rt0=build(1,n); init(rt0); for(qp=_();qp;--qp){ int o=_s(); if(o=='M'){ int x=_(),y=_(); rts[x]=merge(rts[x],rts[y]); rts[y]=0; }else if(o=='S'){ int x=_(); _d=_();_x=_(); split(rts[x],rts[n+1],rts[n+2]); rts[x]=0; n+=2; }else if(o=='Q'){ rts[_()]->query(); }else{ int x=_(); rts[x]->add(_()); } } return 0; }