[BZOJ3123][Sdoi2013]森林(主席树+启发式合并)
2016-12-18 17:48
471 查看
=== ===
这里放传送门=== ===
题解
这题好像很久以前就看过了然后也发现是主席树的启发式合并但是写了半天以后就写得乱七八糟非常愚蠢然后就弃了。。然后前几天又拿出来重写才搞掉它。。首先对于Q操作,树上主席树就能解决问题;但是如果要把两个连通块连在一起,就要把它们所在的主席树合并。这样的话用启发式合并就可以保证科学的时间复杂度,每次把规模较小的合并到规模较大的里面,这样每一次每个点所在的连通块size都会翻倍,这样的翻倍最多进行O(logn)次,加上每次重新insert带着的一个log,总的时间复杂度是O(log2n)的。
然后就是实现方面的问题了。。以前写树上主席树的时候都是先dfs一遍处理出dfs序再跑到主函数里按照dfs序的顺序建树的,但是如果是这个题的话每次维护dfs序也非常麻烦,所以直接在dfs的过程里面建树就可以了,非常方便其实。。需要注意的一个事情就是每次要把L操作新增的边也加到邻接表里面去,要不然下次再遍历这个连通块的时候就遍历不到新连过去的这块了。。
这题空间耗费出奇的大。。因为没有写垃圾回收之类的东西。。
代码
#include<cstdio> #include<cstring> #include<algorithm> #define Pow 17 using namespace std; int n,m,T,v[80010],p[80010],a[160010],nxt[160010],root[80010],num[80010],father[80010],deep[80010]; int tot,cnt,size,f[80010][19],anti[80010],lastans,w[80010]; struct segtree{ int l,r,val; }t[10000010]; void add(int x,int y){ tot++;a[tot]=y;nxt[tot]=p[x];p[x]=tot; } int comp(int x,int y){return w[x]<w[y];} int find(int x){ if (x==father[x]) return x; father[x]=find(father[x]); return father[x]; } void insert(int &i,int j,int l,int r,int x){ i=++size;t[i]=t[j];t[i].val++; if (l==r) return; int mid=(l+r)>>1; if (x<=mid) insert(t[i].l,t[j].l,l,mid,x); else insert(t[i].r,t[j].r,mid+1,r,x); } void dfs(int u,int fa){ deep[u]=deep[fa]+1; for (int i=1;i<=Pow;i++) f[u][i]=f[f[u][i-1]][i-1]; insert(root[u],root[fa],1,cnt,v[u]); for (int i=p[u];i!=0;i=nxt[i]) if (a[i]!=fa){ f[a[i]][0]=u; dfs(a[i],u); } } int find_lca(int x,int y){ if (deep[x]!=deep[y]){ if (deep[x]<deep[y]) swap(x,y); for (int i=Pow;i>=0;i--) if (deep[f[x][i]]>=deep[y]) x=f[x][i]; } for (int i=Pow;i>=0;i--) if (f[x][i]!=f[y][i]){ x=f[x][i];y=f[y][i]; } while (x!=y){ x=f[x][0];y=f[y][0]; } return x; } int query(int i,int j,int lca,int flca,int l,int r,int k){ if (l==r) return l; int mid=(l+r)>>1,val; val=t[t[i].l].val+t[t[j].l].val-t[t[lca].l].val-t[t[flca].l].val; if (k<=val) return query(t[i].l,t[j].l,t[lca].l,t[flca].l,l,mid,k); else return query(t[i].r,t[j].r,t[lca].r,t[flca].r,mid+1,r,k-val); } int main() { scanf("%d",&T); scanf("%d%d%d",&n,&m,&T); for (int i=1;i<=n;i++){ scanf("%d",&w[i]);p[i]=i; } sort(p+1,p+n+1,comp); for (int i=1;i<=n;i++){ if (w[p[i]]==w[p[i-1]]) v[p[i]]=cnt; else v[p[i]]=++cnt; anti[cnt]=w[p[i]]; } for (int i=1;i<=n;i++){ father[i]=i;num[i]=1; } memset(p,0,sizeof(p)); for (int i=1;i<=m;i++){ int x,y,r1,r2; scanf("%d%d",&x,&y); add(x,y);add(y,x); r1=find(x);r2=find(y); if (r1!=r2){ father[r2]=r1; num[r1]+=num[r2]; } } for (int i=1;i<=n;i++) if (deep[i]==0){ int rt=find(i); dfs(rt,0); } for (int i=1;i<=T;i++){ char c=getchar(); int x,y,k,lca; while (c!='L'&&c!='Q') c=getchar(); scanf("%d%d",&x,&y); x^=lastans;y^=lastans; if (c=='Q'){ scanf("%d",&k); k^=lastans; lca=find_lca(x,y); lastans=query(root[x],root[y],root[lca],root[f[lca][0]],1,cnt,k); lastans=anti[lastans]; printf("%d\n",lastans); }else{ int r1,r2; add(x,y);add(y,x);//每次都要加边以便下次遍历 r1=find(x);r2=find(y); if (num[r1]<num[r2]){ swap(x,y);swap(r1,r2); } father[r2]=r1;num[r1]+=num[r2]; f[y][0]=x;dfs(y,x); } } return 0; }
相关文章推荐
- Bzoj 3123: [Sdoi2013]森林(主席树+启发式合并)
- [BZOJ3123][Sdoi2013]森林(主席树+启发式合并)
- [BZOJ3123][Sdoi2013]森林(主席树启发式合并)
- BZOJ 3123: [Sdoi2013]森林 [主席树启发式合并]
- BZOJ 3123: [Sdoi2013]森林 启发式合并 树上主席树
- 【BZOJ3123】[Sdoi2013]森林 主席树+倍增LCA+启发式合并
- [bzoj3123][sdoi2013森林] (树上主席树+lca+并查集启发式合并+暴力重构森林)
- 【bzoj3123】【sdoi2013】【森林】【启发式合并+主席树】
- [主席树 启发式合并] BZOJ 3123 [Sdoi2013]森林
- 【BZOJ 3123】 [Sdoi2013]森林 主席树启发式合并
- [bzoj3123][洛谷P3302] [SDOI2013]森林(树上主席树+倍增lca+启发式合并)
- BZOJ 3123: [Sdoi2013]森林|主席树|启发式合并
- bzoj 3123 [Sdoi2013]森林(主席树,lca,启发式合并)
- BZOJ_3123_[Sdoi2013]森林_主席树+启发式合并
- [bzoj3123][Sdoi2013]森林_主席树_启发式合并
- BZOJ 3123 SDOI 2013 森林 可持久化线段树+启发式合并
- bzoj 3123: [Sdoi2013]森林 启发式合并+可持久化线段树
- 3123: [Sdoi2013]森林 主席树的启发式合并
- bzoj 3123(主席树+启发式合并)
- 【BZOJ3123】森林(主席树,启发式合并)