BZOJ 3123: [Sdoi2013]森林
3123: [Sdoi2013]森林
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2914 Solved: 853
[Submit][Status][Discuss]
Description
Input
第一行包含一个正整数testcase,表示当前测试数据的测试点编号。保证1≤testcase≤20。
第二行包含三个整数N,M,T,分别表示节点数、初始边数、操作数。第三行包含N个非负整数表示 N个节点上的权值。
接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y ”,其含义见题目描述部分。
Output
对于每一个第一类操作,输出一个非负整数表示答案。
Sample Input
1
8 4 8
1 1 2 2 3 3 4 4
4 7
1 8
2 4
2 1
Q 8 7 3 Q 3 5 1
Q 10 0 0
L 5 4
L 3 2 L 0 7
Q 9 2 5 Q 6 1 6
8 4 8
1 1 2 2 3 3 4 4
4 7
1 8
2 4
2 1
Q 8 7 3 Q 3 5 1
Q 10 0 0
L 5 4
L 3 2 L 0 7
Q 9 2 5 Q 6 1 6
Sample Output
2
2
1
4
2
2
1
4
2
HINT
对于第一个操作 Q 8 7 3,此时 lastans=0,所以真实操作为Q 8^0 7^0 3^0,也即Q 8 7 3。点8到点7的路径上一共有5个点,其权值为4 1 1 2 4。这些权值中,第三小的为 2,输出 2,lastans变为2。对于第二个操作 Q 3 5 1 ,此时lastans=2,所以真实操作为Q 3^2 5^2 1^2 ,也即Q 1 7 3。点1到点7的路径上一共有4个点,其权值为 1 1 2 4 。这些权值中,第三小的为2,输出2,lastans变为 2。之后的操作类似。
Source
分析:
如果没有L操作就很好办了,直接维护每个节点的到根节点的路径上的权值线段树,然后加加减减就好了...
现在有L操作就是说我们要合并两棵树以及更新每个节点的信息,那么因为要更新信息,所以一定是要遍历一遍某课子树,所以我们采用启发式合并主席树,每一次重新dfs小的那棵树重建每个节点的权值权值线段树...
时间复杂度$O(Nlog^{2}N)$...然而我的比别人的跑的都慢...加了快读才勉强卡过去...
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> //by NeighThorn using namespace std; const int maxn=200000+5,maxm=40000000+5; int n,m,t,T,cas,cnt,len,tot,w[maxn],mp[maxn],ls[maxm],rs[maxm],hd[maxn],fa[maxn][25],to[maxn],nxt[maxn],sum[maxm],dep[maxn],siz[maxn],vis[maxn],root[maxn],father[maxn]; inline int read(void){ char ch=getchar();int x=0,f=1; while(!(ch>='0'&&ch<='9')){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return f*x; } inline int Find(int x){ return father[x]==x?x:father[x]=Find(father[x]); } inline int find(int x){ int _=1,__=len,___; while(_<=__){ int ____=(_+__)>>1; if(mp[____]>=x) ___=____,__=____-1; else _=____+1; } return ___; } inline void add(int x,int y){ to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++; } inline void change(int l,int r,int x,int &y,int val){ y=++tot,sum[y]=sum[x]+1; if(l==r) return; int mid=(l+r)>>1;ls[y]=ls[x],rs[y]=rs[x]; if(val<=mid) change(l,mid,ls[x],ls[y],val); else change(mid+1,r,rs[x],rs[y],val); } inline int LCA(int x,int y){ if(dep[x]<dep[y]) swap(x,y); int d=dep[x]-dep[y]; for(int i=0;i<=20;i++) if((d>>i)&1) x=fa[x][i]; if(x==y) return x; for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } inline void dfs(int rt){ vis[rt]=T;change(1,len,fa[rt][0]==rt?root[0]:root[fa[rt][0]],root[rt],find(w[rt])); for(int j=1;j<=20;j++) fa[rt][j]=fa[fa[rt][j-1]][j-1]; for(int i=hd[rt];i!=-1;i=nxt[i]) if(vis[to[i]]!=T) fa[to[i]][0]=rt,dep[to[i]]=dep[rt]+1,dfs(to[i]); } inline int query(int l,int r,int x,int y,int lca,int flca,int num){ if(l==r) return l; int mid=(l+r)>>1; if(sum[ls[x]]+sum[ls[y]]-sum[ls[lca]]-sum[ls[flca]]>=num) return query(l,mid,ls[x],ls[y],ls[lca],ls[flca],num); else return query(mid+1,r,rs[x],rs[y],rs[lca],rs[flca],num-(sum[ls[x]]+sum[ls[y]]-sum[ls[lca]]-sum[ls[flca]])); } signed main(void){ cas=read(); cnt=0;len=0;tot=0;T=1; memset(hd,-1,sizeof(hd)); n=read();m=read();t=read(); for(int i=1;i<=n;i++) w[i]=read(),mp[i]=w[i]; sort(mp+1,mp+n+1);len=unique(mp+1,mp+n+1)-mp-1; for(int i=1,x,y;i<=m;i++) x=read(),y=read(),add(x,y),add(y,x); for(int i=1;i<=n;i++) father[i]=i,siz[i]=1; for(int i=1;i<=n;i++) if(vis[i]!=T) dep[i]=0,fa[i][0]=i,dfs(i); T++;char opt[3];int ans=0; for(int i=1,s,x,y;i<=t;i++){ scanf("%s",opt); if(opt[0]=='Q'){ x=read(),y=read(),s=read();x=x^ans,y=y^ans,s=s^ans; int lca=LCA(x,y); printf("%d\n",ans=mp[query(1,len,root[x],root[y],root[lca],fa[lca][0]==lca?root[0]:root[fa[lca][0]],s)]); } else{ x=read(),y=read();x=x^ans,y=y^ans; int fx=Find(x),fy=Find(y); if(siz[fx]>siz[fy]) swap(x,y),swap(fx,fy); fa[x][0]=y,dep[x]=dep[y]+1,dfs(x),add(x,y),add(y,x);father[fx]=fy,siz[fy]+=siz[fx];T++; } } return 0; }
By NeighThorn