B20J_2733_[HNOI2012]永无乡_权值线段树合并
Description:
n座岛,编号从1到n,每座岛都有自己的独一无二的重要度,按照重要度可以将这n座岛排名,名次用1到 n来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。现在有两种操作:B x y表示在岛 x与岛y之间修建一座新桥。Q x k表示询问当前与岛 x连通的所有岛中第k重要的是哪座岛,即所有与岛 x连通的岛中重要度排名第 k小的岛是哪座,请你输出那个岛的编号。
对于100%的数据n≤100000,m≤n,q≤300000。
分析:读懂题后发现是一道线段树合并的裸题。Q操作显然是权值线段树求区间第k小元素,B操作是合并。
直接开发现开不下,需要动态开点,一开始要开nlogn个结点。
合并操作:
int merge(int x,int y) { if(!x)return y; if(!y)return x; lson[x]=merge(lson[x],lson[y]); rson[x]=merge(rson[x],rson[y]); t[x]=t[lson[x]]+t[rson[x]]; return x; }
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int N=3262145; int tree[N],lson[N],rson[N],t[N],mp[N],fa[N],idx[N],cnt; int n,m,k; char s[10]; int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } void bt(int l,int r,int val,int &pos) { if(pos==0)pos=++cnt; if(l==r) { t[pos]=1; return ; } int mid=l+r>>1; if(val<=mid)bt(l,mid,val,lson[pos]); else bt(mid+1,r,val,rson[pos]); t[pos]=t[lson[pos]]+t[rson[pos]]; } int merge(int x,int y) { if(!x)return y; if(!y)return x; lson[x]=merge(lson[x],lson[y]); rson[x]=merge(rson[x],rson[y]); t[x]=t[lson[x]]+t[rson[x]]; return x; } int query(int l,int r,int k,int pos) { if(l==r||k==0) { return mp[l]; } int mid=l+r>>1; if(t[pos]<k)return -1; if(t[lson[pos]]>=k) { return query(l,mid,k,lson[pos]); } else { return query(mid+1,r,k-t[lson[pos]],rson[pos]); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { fa[i]=i; } for(int i=1;i<=n;i++) { scanf("%d",&idx[i]); mp[idx[i]]=i; } for(int i=1;i<=n;i++) { tree[i]=++cnt; bt(1,n,idx[i],tree[i]); } int x,y; for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); int dx=find(x),dy=find(y); if(dx!=dy) { fa[dy]=dx; tree[dx]=merge(tree[dx],tree[dy]); } } scanf("%d",&k); while(k--) { scanf("%s%d%d",s,&x,&y); int dx=find(x); if(s[0]=='Q') { printf("%d ",query(1,n,y,tree[dx])); } else { int dx=find(x),dy=find(y); if(dx!=dy) { fa[dy]=dx; tree[dx]=merge(tree[dx],tree[dy]); } } } }