这个题目我是建立一个操作树,根据时间的关系。 合并的过程还是用并查集来实现(按秩合并),然后关于询问,我是这么处理的,对于每个联通块,我建立一个权值分块,即cnt数组
由于内存限制,所以我权值的块的大小是1200,块数是最多90,每次递归的时候,就增加cnt,递归回来的时候,就减去cnt,这样就在操作树上实现了添加和删除操作。
注意这里的并查集是不能路径压缩的。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define bl(x) ((x-1)/B+1) 4 #define st(x) ((x-1)*B+1) 5 #define ed(x) min(n,x*B) 6 int const N=100000+10; 7 int const B=1200; 8 int f[N],a[N],b[N],c[N],cnt[N][90],n,m,sum,h[N],sz[N],ans[N]; 9 struct edge{int to,nt;}e[N]; 10 struct query{int opt,x,y,id;}q[N]; 11 int cmp(int x,int y){return a[x]<a[y];} 12 int gf(int x){return x==f[x]? x:gf(f[x]); } 13 void add(int a,int b){ e[++sum].to=b; e[sum].nt=h[a]; h[a]=sum; } 14 int query(int x,int y){ 15 int fx=gf(x),k=1; 16 for(int i=1;i<90;i++,k++) 17 if(y>cnt[fx][i]) y-=cnt[fx][i]; 18 else break; 19 if(k==90) return -1; 20 for(int i=st(k);i<=ed(k);i++) 21 if(gf(b[i])==fx){ 22 if(!--y) { 23 return a[b[i]]; 24 } 25 } 26 } 27 void dfs(int x){ 28 int fx,fy; 29 if(q[x].opt==1){ 30 fx=gf(q[x].x); 31 fy=gf(q[x].y); 32 if(sz[fx]>sz[fy]) 33 swap(fx,fy); 34 if(fx!=fy){ 35 f[fx]=fy; 36 for(int i=1;i<90;i++) cnt[fy][i]+=cnt[fx][i]; 37 sz[fy]+=sz[fx]; 38 } 39 } 40 if(q[x].opt==3){ 41 ans[q[x].id]=query(q[x].x,q[x].y); 42 } 43 for(int i=h[x];i;i=e[i].nt) 44 dfs(e[i].to); 45 if(q[x].opt==1){ 46 if(fx!=fy){ 47 f[fx]=fx; 48 sz[fy]-=sz[fx]; 49 for(int i=1;i<90;i++) cnt[fy][i]-=cnt[fx][i]; 50 } 51 } 52 } 53 int main(){ 54 scanf("%d%d",&n,&m); 55 for(int i=1;i<=n;i++) 56 scanf("%d",&a[i]); 57 for(int i=1;i<=n;i++) b[i]=i; 58 sort(b+1,b+n+1,cmp); 59 for(int i=1;i<=n;i++) c[b[i]]=i; 60 for(int i=1;i<=m;i++){ 61 int opt,x,y; 62 scanf("%d%d",&q[i].opt,&q[i].x); 63 q[i].id=i; 64 if(q[i].opt==1){ 65 scanf("%d",&q[i].y); 66 add(i-1,i); 67 } 68 if(q[i].opt==2){ 69 add(q[i].x,i); 70 } 71 if(q[i].opt==3){ 72 add(i-1,i); 73 scanf("%d",&q[i].y); 74 } 75 } 76 for(int i=1;i<=n;i++) 77 f[i]=i,sz[i]=1,cnt[i][bl(c[i])]++; 78 dfs(0); 79 for(int i=1;i<=m;i++) 80 if(q[i].opt==3) printf("%d\n",ans[i]); 81 return 0; 82 }View Code