[bzoj3123][SDOI2013]森林
2016-07-01 12:47
429 查看
Description
给出一片森林,要求维护以下操作。Q x,y,k 询问x到y的路径上权值第k小的是什么。
L x,y 在x,y之间连一条边,保证操作之后原图仍是一片森林。
强制在线。
n<=8*10^4,权值<=10^9
Solution
看到求k小值,立马想到主席树。(Chair_Man Tree大法好)那么询问操作很简单。
那么合并呢?
启发式合并!
也就是选择要合并的两棵树中较小的那一棵塞到较大的那一棵里面去。
这样做的复杂度是均摊O(nlogn)的。(不要问为什么)
然后加上个离散化省点空间。
然后就没有了。(启发式合并是个好东西,(⊙v⊙)嗯)
Code
#include<cstdio> #include<cstring> #include<algorithm> #define fo(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) #define rep(i,a) for(int i=last[a];i;i=next[i]) #define N 80005 using namespace std; struct tree{int l,r,size;}tr[N*300]; struct note{int v,w;}a ; bool cmp(note x,note y) {return x.v<y.v;} int n,m,q,ty,l,x,y,k,tot,cnt,ans,size ,root ,f [17]; int last ,next[N*2],t[N*2],rt ,c ,d ,h ; char ch[1]; void add(int x,int y) { t[++l]=y;next[l]=last[x];last[x]=l; } void change(int &v,int l,int r,int x) { tr[++tot]=tr[v];tr[tot].size++;v=tot; if (l==r) return; int m=(l+r)/2; if (x<=m) change(tr[v].l,l,m,x); else change(tr[v].r,m+1,r,x); } void dfs(int x,int y) { root[x]=root[y]; change(root[x],1,cnt,c[x]); f[x][0]=y;d[x]=d[y]+1;size[x]=1; fo(j,1,16) f[x][j]=f[f[x][j-1]][j-1]; rep(i,x) if (t[i]!=y) dfs(t[i],x),size[x]+=size[t[i]]; } void make(int x,int y,int z) { size[x]=y;rt[x]=z; rep(i,x) if (t[i]!=f[x][0]) make(t[i],y,z); } int lca(int x,int y) { if (d[x]<d[y]) swap(x,y); fd(j,16,0) if (d[f[x][j]]>d[y]) x=f[x][j]; if (d[x]!=d[y]) x=f[x][0]; fd(j,16,0) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j]; if (x!=y) return f[x][0];else return x; } int find(int x,int y,int z,int f,int l,int r,int id) { if (l==r) return l; int m=(l+r)/2; int lx=tr[x].l,rx=tr[x].r; int ly=tr[y].l,ry=tr[y].r; int lz=tr[z].l,rz=tr[z].r; int lf=tr[f].l,rf=tr[f].r; if (tr[lx].size+tr[ly].size-tr[lz].size-tr[lf].size>=id) return find(lx,ly,lz,lf,l,m,id);else return find(rx,ry,rz,rf,m+1,r,id-tr[lx].size-tr[ly].size+tr[lz].size+tr[lf].size); } void merge(int x,int y) { add(x,y);add(y,x); if (size[x]>size[y]) swap(x,y); dfs(x,y);make(rt[y],size[x]+size[y],rt[y]); } int main() { scanf("%d%d%d%d",&n,&n,&m,&q); fo(i,1,n) scanf("%d",&a[i].v),a[i].w=i; sort(a+1,a+n+1,cmp); fo(i,1,n) { if (a[i].v!=a[i-1].v) h[++cnt]=a[i].v; c[a[i].w]=cnt; } fo(i,1,m) scanf("%d%d",&x,&y),add(x,y),add(y,x); fo(i,1,n) if (!root[i]) dfs(i,0),make(i,size[i],i); for(;q;q--) { scanf("%s%d%d",ch,&x,&y);x^=ans;y^=ans; if (ch[0]=='Q') { int z=lca(x,y);scanf("%d",&k);k^=ans; ans=h[find(root[x],root[y],root[z],root[f[z][0]],1,cnt,k)]; printf("%d\n",ans); } else merge(x,y); } }
相关文章推荐
- windows 80端口被占用
- java分布式应用之初:实现系统间通信方式简介
- footer绝对定位但是不在页面最下边解决方案
- LeetCode:Next Permutation
- leetcode 100. Same Tree
- [3.0.1]性能调优之调节并行度
- 矩阵分解(rank decomposition)文章代码汇总
- linxu学习之android(一):linux下安装android studio 、android sdk安装教程
- MVC MVP 和 MVVM 的图示
- Android中的坐标系以及获取坐标的方法
- 获取用户SID
- NSNotification调用机制
- python -正则表达式
- 土地购买[Usaco2008 Mar][bzoj 1597]
- js如何判断IE浏览器的版本包括IE11
- java中for和foreach的区别
- JAVA基础知识篇
- Android白话启动篇(Android booting process)
- python判断字符串编码的简单实现方法(使用chardet)
- 服务器TIME_WAIT和CLOSE_WAIT详解和解决办法