1. 程式人生 > >[HNOI2012] 永無鄉 題解

[HNOI2012] 永無鄉 題解

ret sof 線段樹 ++i mic 相同 tro strong getc

題意:

  n個點,有加邊操作,詢問與某一點處於相同的聯通塊的點中權值第k大的點

思路:

  對所有點建立一棵權值線段樹,加邊就配合並查集進行線段樹合並

反思:

  動態開點,權值線段樹要用sum[g[x=find(x)]](還是不夠熟練),g為根。

代碼:

 1 #include<cstdio>
 2 const int M=100005,N=5500000;
 3 int sz,a[M],p[M],g[M],id[M],lc[N],rc[N],sum[N];
 4 char c[5];
 5 
 6 int read()
 7 {
 8     int x=0; char ch=getchar();
9 while (ch<48 || ch>57) ch=getchar(); 10 while (ch>47 && ch<58) x=(x<<1)+(x<<3)+ch-48,ch=getchar(); 11 return x; 12 } 13 14 int find(int x) { for (;x^p[x];x=p[x]=p[p[x]]); return x; } 15 16 void add(int l,int r,int &k,int x) 17 { 18 if (!k) k=++sz; ++sum[k];
19 if (l==r) return; 20 int mid=l+r>>1; 21 if (x>mid) add(mid+1,r,rc[k],x); 22 else add(l,mid,lc[k],x); 23 } 24 25 int merge(int x,int y) 26 { 27 if (!x || !y) return x|y; 28 lc[x]=merge(lc[x],lc[y]),rc[x]=merge(rc[x],rc[y]); 29 sum[x]=sum[lc[x]]+sum[rc[x]]; 30 return
x; 31 } 32 33 int ask(int l,int r,int cur,int k) 34 { 35 if (l==r) return l; 36 int mid=l+r>>1; 37 if (sum[lc[cur]]<k) return ask(mid+1,r,rc[cur],k-sum[lc[cur]]); 38 else return ask(l,mid,lc[cur],k); 39 } 40 41 int main() 42 { 43 int n=read(),m=read(),i,x,y; 44 for (i=1;i<=n;++i) id[a[i]=read()]=i,p[i]=i; 45 for (i=1;i<=m;++i) p[find(read())]=find(read()); 46 for (i=1;i<=n;++i) add(1,n,g[find(i)],a[i]); 47 for (m=read();m--;) 48 if (scanf("%s",c),x=read(),y=read(),c[0]==Q) 49 if (sum[g[x=find(x)]]<y) puts("-1"); 50 else printf("%d\n",id[ask(1,n,g[x],y)]); 51 else 52 if ((x=find(x))^(y=find(y))) 53 if (sum[y]<sum[x]) p[y]=x,g[x]=merge(g[x],g[y]); 54 else p[x]=y,g[y]=merge(g[y],g[x]); 55 return 0; 56 }

[HNOI2012] 永無鄉 題解