[bzoj2733][HNOI2012]永無鄉_權值線段樹_線段樹合併
阿新 • • 發佈:2018-12-23
永無鄉 bzoj-2733 HNOI-2012
題目大意:題目連結。
註釋:略。
想法:
它的查詢操作非常友善,就是一個聯通塊內的$k$小值。
故此我們可以考慮每個聯通塊建一棵權值線段樹。
這樣的話每次修改採用線段樹啟發式合併,查詢暴力走權值線段樹即可。
Code:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define N 100010 using namespace std; struct Node { int ls,rs,size; Node() {ls=rs=size=0;} }a[N*50]; int rt[N],fa[N],cnt,val[N],re[N]; int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);} int merge(int x,int y) { if(!x||!y) return x|y; a[x].size+=a[y].size; a[x].ls=merge(a[x].ls,a[y].ls); a[x].rs=merge(a[x].rs,a[y].rs); return x; } int query(int x,int k,int l,int r) { if(l==r) return l; int ls=a[x].ls,rs=a[x].rs; int mid=(l+r)>>1; if(k<=a[ls].size) return query(ls,k,l,mid); else return query(rs,k-a[ls].size,mid+1,r); } int build(int x,int l,int r) { // printf("%d %d %d\n",x,l,r); int p=++cnt; a[p].size=1; if(l==r) return p; int mid=(l+r)>>1; if(x<=mid) a[p].ls=build(x,l,mid); else a[p].rs=build(x,mid+1,r); return p; } int main() { int n,m; cin >> n >> m ; for(int i=1;i<=n;i++) scanf("%d",&val[i]),re[val[i]]=i,fa[i]=i; for(int i=1;i<=n;i++) rt[i]=build(val[i],1,n); // for(int i=1;i<=n;i++) cout << rt[i] << " " ; puts(""); for(int x,y,i=1;i<=m;i++) { scanf("%d%d",&x,&y); x=find(x); y=find(y); if(x!=y) { rt[x]=merge(rt[x],rt[y]); fa[y]=x; } } // for(int i=1;i<=n;i++) printf("%d ",find(i)); puts(""); int q; cin >> q ; while(q--) { char opt[10]; int x,y; scanf("%s%d%d",opt,&x,&y); if(opt[0]=='B') { x=find(x); y=find(y); if(x!=y) { rt[x]=merge(rt[x],rt[y]); fa[y]=x; } } else { x=find(x); if(y>a[rt[x]].size) puts("-1"); else printf("%d\n",re[query(rt[x],y,1,n)]); } } return 0; }
小結:這題是別人好幾個月之前寫的,當時覺得賊高階現在一看原來是sb題.....