[SDOI2013]森林(树上主席树)
Posted hsez-cyx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2013]森林(树上主席树)相关的知识,希望对你有一定的参考价值。
Description
题目描述
小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有M条边。
小Z希望执行T个操作,操作有两类:
Q x y k
查询点x到点y路径上所有的权值中,第k小的权值是多少。此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点。L x y
在点x和点y之间连接一条边。保证完成此操作后,仍然是一片森林。
为了体现程序的在线性,我们把输入数据进行了加密。设lastans为程序上一次输出的结果,初始的时候lastans为0。
- 对于一个输入的操作
Q x y k
,其真实操作为Q x^lastans y^lastans k^lastans
。 - 对于一个输入的操作
L x y
,其真实操作为L x^lastans y^lastans
。其中^运算符表示异或,等价于pascal中的xor运算符。
请写一个程序來帮助小Z完成这些操作。
对于所有的数据,n,m,T<=8*10^48∗104.
输入格式
第一行包含一个正整数testcase,表示当前测试数据的测试点编号。保证1<=testcase<=20。
第二行包含三个整数N,M,T,分别表示节点数、初始边数、操作数。
第三行包含N个非负整数表示 N个节点上的权值。
接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边。
接下来 T行,每行描述一个操作,格式为”Q x y k“或者”L x y “,其含义见题目描述部分。
输出格式
对于每一个第一类操作,输出一个非负整数表示答案。
Solution
对于每个点建立一棵权值线段树,将 它 到 它所在树的根 的路径上每个点的权值放入线段树中
并利用它的父亲的线段树的一部分来优化时间空间
每次连边操作用启发式合并来把复杂度优化到O(logn),修改较小数所有节点的权值线段树,方法同建树时
每次询问操作找lca,再将它们的权值线段树相减,在上面找第k大(注意对lca的处理)
Code
#include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> using namespace std; const int N=8e4+10; struct node { int v,id; bool operator <(const node &o)const { return v<o.v; } }a[N]; int d[N],n,m,T,cnt,re[N],u,v,k,fa[N][17],top[N],si[N],rt[N],tot,dep[N]; vector <int> link[N]; bool flag[N]; char s[5]; struct mode { int lc,rc,sum; }f[N*400]; void build(int l,int r,int &x,int y,int pos) { x=++tot,f[x]=f[y],f[x].sum++; if(l==r) return ; int mid=(l+r)>>1; if(pos<=mid) build(l,mid,f[x].lc,f[y].lc,pos); else build(mid+1,r,f[x].rc,f[y].rc,pos); } void dfs(int u,int fx) { flag[u]=true; fa[u][0]=fx,si[u]=1,dep[u]=dep[fx]+1; for(int i=1;i<17;i++) fa[u][i]=fa[fa[u][i-1]][i-1]; build(1,cnt,rt[u],rt[fx],d[u]); int size=link[u].size(); for(int i=size-1;i>=0;i--) { int v=link[u][i]; if(v==fx) continue; top[v]=top[u]; dfs(v,u),si[u]+=si[v]; } } int get_top(int x) { return x==top[x]?x:top[x]=get_top(top[x]); } int Lca(int x,int y) { if(dep[x]>dep[y]) swap(x,y); for(int i=16;i>=0;i--) if(dep[fa[y][i]]>=dep[x]) y=fa[y][i]; if(y==x) return x; for(int i=16;i>=0;i--) if(fa[y][i]!=fa[x][i]) y=fa[y][i],x=fa[x][i]; return fa[x][0]; } int get(int lca1,int lca2,int x,int y,int l,int r,int k) { if(l==r) return l; int mid=(l+r)>>1; int cn=f[f[x].lc].sum+f[f[y].lc].sum-f[f[lca1].lc].sum-f[f[lca2].lc].sum; if(k<=cn) return get(f[lca1].lc,f[lca2].lc,f[x].lc,f[y].lc,l,mid,k); else return get(f[lca1].rc,f[lca2].rc,f[x].rc,f[y].rc,mid+1,r,k-cn); } int main() { scanf("%d",&T); scanf("%d%d%d",&n,&m,&T); for(int i=1;i<=n;i++) scanf("%d",&a[i].v),a[i].id=i; sort(a+1,a+1+n); for(int i=1;i<=n;i++) { if(i==1 || a[i].v!=a[i-1].v) re[++cnt]=a[i].v; d[a[i].id]=cnt; } while(m--) { scanf("%d%d",&u,&v); link[u].push_back(v); link[v].push_back(u); } for(int i=1;i<=n;i++) if(!flag[i]) top[i]=i,dfs(i,0); int last=0; while(T--) { scanf("%s%d%d",s,&u,&v); u^=last,v^=last; if(s[0]==‘L‘) { link[u].push_back(v); link[v].push_back(u); int fu=get_top(u),fv=get_top(v); if(si[fu]<si[fv]) swap(fu,fv),swap(u,v); top[v]=fu,si[fu]+=si[fv]; dfs(v,u); } else { scanf("%d",&k); k^=last; int lca=Lca(u,v); last=re[get(rt[fa[lca][0]],rt[lca],rt[u],rt[v],1,cnt,k)]; printf("%d ",last); } } return 0; }
以上是关于[SDOI2013]森林(树上主席树)的主要内容,如果未能解决你的问题,请参考以下文章