[bzoj3123][Sdoi2013]森林_主席树_启发式合并
森林 bzoj-3123 Sdoi-2013
题目大意:给定一片共n个点的森林,T个操作,支持:连接两个不在一棵树上的两个点;查询一棵树上路径k小值。
注释:$1\le n,T \le 8\cdot 10^4$
想法:运用冯老师讲的方法:
“对于一个开起来非常困难的问题,我们可以通过先构造拟对象,然后向完全对象转化”
这个题,我们看到了最后一个操作,想到了主席树。
两个点x、y之间的路径,我们在主席树上用root[x]+root[y]-root[lca(x,y)]-root[fa[lca(x,y)]]的权值线段树即可。
那么,如果加上连接操作呢?
直接启发式合并,然后重置一部分的主席树即可。(重置的时候记得把倍增的数组也重置)
最后,附上丑陋的代码... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <map> using namespace std; int cnt,tot,ans,len,a,b,tcase,n,m,q; char s[5]; int x,y,z,d[80010],h[80010],v[80010]; map<int,int>g; int to[200010],vis[80010],anc[80010],size[80010],ls[20000010],rs[20000010],nxt[200010],head[200010],f[80010][26],sum[20000010],root[2000010]; inline void add(int x,int y) { to[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } int lca(int x,int y) { if(d[x]<d[y]) { swap(x,y); } int dep=d[x]-d[y]; for(int i=0;i<=23;i++) { if((dep&(1<<i))!=0) { x=f[x][i]; } } if(x==y) { return x; } for(int i=23;i>=0;i--) { if(f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } } return f[x][0]; } int updata(int pre,int l,int r,int v) { int pos=++cnt,mid=(l+r)>>1; ls[pos]=ls[pre]; rs[pos]=rs[pre]; sum[pos]=sum[pre]+1; if(l==r) { return pos; } else { if(v<=mid) { ls[pos]=updata(ls[pre],l,mid,v); } else { rs[pos]=updata(rs[pre],mid+1,r,v); } } return pos; } int query(int x,int y,int fa,int anc,int l,int r,int k) { int mid=(l+r)>>1; if(l==r) { return g[l]; } int num=sum[ls[x]]+sum[ls[y]]-sum[ls[fa]]-sum[ls[anc]]; if(k<=num) { return query(ls[x],ls[y],ls[fa],ls[anc],l,mid,k); } else { return query(rs[x],rs[y],rs[fa],rs[anc],mid+1,r,k-num); } } void dfs(int x,int fa,int ac) { vis[x]=1; anc[x]=ac; f[x][0]=fa; d[x]=d[fa]+1; root[x]=updata(root[fa],1,n,v[x]); for(int i=1;i<=23;i++) { f[x][i]=f[f[x][i-1]][i-1]; } for(int i=head[x];i;i=nxt[i]) { if(to[i]!=fa) { dfs(to[i],x,ac); size[x]+=size[to[i]]; } } } int main() { scanf("%d",&tcase); scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=n;i++) { scanf("%d",&v[i]); h[i]=v[i]; size[i]=1; } sort(h+1,h+1+n); len=unique(h+1,h+1+n)-h-1; for(int i=1;i<=n;i++) { int val=v[i]; v[i]=lower_bound(h+1,h+1+len,v[i])-h; g[v[i]]=val; } for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(int i=1;i<=n;i++) { if(vis[i]) continue; dfs(i,0,i); } while(q--) { scanf("%s",s); if(s[0]=='Q') { scanf("%d%d%d",&x,&y,&z); x=x^ans; y=y^ans; z=z^ans; a=lca(x,y); b=f[a][0]; ans=query(root[x],root[y],root[a],root[b],1,n,z); printf("%d\n",ans); } else { scanf("%d%d",&x,&y); x=x^ans; y=y^ans; add(x,y); add(y,x); if(size[anc[x]]<size[anc[y]]) { size[anc[y]]+=size[anc[x]]; dfs(x,y,anc[y]); } else { size[anc[x]]+=size[anc[y]]; dfs(y,x,anc[x]); } } } }
小结:主席树活用起来真tm吓人... ...
| 欢迎来原网站坐坐! >原文链接<