BZOJ3123:[SDOI2013]森林(主席树,启发式合并)
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。之后的操作类似。
Solution
可以发现没有连边那个操作就是裸的把主席树放到树上跑……QAQ 具体操作可以看我这篇博客
然后又发现只有连边没有删边……那么就可以直接每次连边就启发式合并两棵树,对小的那颗暴力全部重建主席树。复杂度$nlog^2n$
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 #define N (80009) 6 using namespace std; 7 8 struct Sgt{int ls,rs,val;}Segt[N<<7]; 9 struct Edge{int to,next;}edge[N<<1]; 10 int head[N],num_edge; 11 int testcase,n,m,q,u,v,num,x,y,k,lca,sgt_num,lastans; 12 int Root[N],a[N],b[N],Depth[N],f[N][18],vis[N],Top[N],Size[N]; 13 char opt[2]; 14 15 int getid(int x) {return lower_bound(b+1,b+n+1,x)-b;} 16 17 void add(int u,int v) 18 { 19 edge[++num_edge].to=v; 20 edge[num_edge].next=head[u]; 21 head[u]=num_edge; 22 } 23 24 int Update(int pre,int l,int r,int x) 25 { 26 int now=++sgt_num; 27 Segt[now].val=Segt[pre].val+1; 28 Segt[now].ls=Segt[pre].ls; 29 Segt[now].rs=Segt[pre].rs; 30 if (l==r) return now; 31 int mid=(l+r)>>1; 32 if (x<=mid) Segt[now].ls=Update(Segt[now].ls,l,mid,x); 33 else Segt[now].rs=Update(Segt[now].rs,mid+1,r,x); 34 return now; 35 } 36 37 int Build(int l,int r) 38 { 39 int now=++sgt_num; 40 if (l==r) return now; 41 int mid=(l+r)>>1; 42 Segt[now].ls=Build(l,mid); 43 Segt[now].rs=Build(mid+1,r); 44 return now; 45 } 46 47 void DFS(int x,int fa,int top) 48 { 49 Size[top]++; Depth[x]=Depth[fa]+1; 50 f[x][0]=fa; Top[x]=top; vis[x]=true; 51 for (int i=1; i<=17; ++i) 52 f[x][i]=f[f[x][i-1]][i-1]; 53 int id=getid(a[x]); 54 Root[x]=Update(Root[fa],1,num,id); 55 for (int i=head[x]; i; i=edge[i].next) 56 if (edge[i].to!=fa) 57 { 58 DFS(edge[i].to,x,top); 59 Size[x]+=Size[edge[i].to]; 60 } 61 } 62 63 int LCA(int x,int y) 64 { 65 if (Depth[x]<Depth[y]) swap(x,y); 66 for (int i=17; i>=0; --i) 67 if (Depth[f[x][i]]>=Depth[y]) x=f[x][i]; 68 if (x==y) return x; 69 for (int i=17; i>=0; --i) 70 if (f[x][i]!=f[y][i]) x=f[x][i], y=f[y][i]; 71 return f[x][0]; 72 } 73 74 int Query(int u,int v,int lca,int flca,int l,int r,int k) 75 { 76 if (l==r) return b[l]; 77 int mid=(l+r)>>1; 78 int x=Segt[Segt[u].ls].val+Segt[Segt[v].ls].val-Segt[Segt[lca].ls].val-Segt[Segt[flca].ls].val; 79 if (k<=x) return Query(Segt[u].ls,Segt[v].ls,Segt[lca].ls,Segt[flca].ls,l,mid,k); 80 else return Query(Segt[u].rs,Segt[v].rs,Segt[lca].rs,Segt[flca].rs,mid+1,r,k-x); 81 } 82 83 int main() 84 { 85 scanf("%d",&testcase); 86 scanf("%d%d%d",&n,&m,&q); 87 for (int i=1; i<=n; ++i) 88 scanf("%d",&a[i]), b[i]=a[i]; 89 sort(b+1,b+n+1); 90 num=unique(b+1,b+n+1)-b-1; 91 Root[0]=Build(1,num); 92 for (int i=1; i<=m; ++i) 93 { 94 scanf("%d%d",&u,&v); 95 add(u,v); add(v,u); 96 } 97 for (int i=1; i<=n; ++i) 98 if (!vis[i]) DFS(i,0,i); 99 for (int Q=1; Q<=q; ++Q) 100 { 101 scanf("%s",opt); 102 if (opt[0]=='Q') 103 { 104 scanf("%d%d%d",&x,&y,&k); 105 x^=lastans; y^=lastans; k^=lastans; 106 int lca=LCA(x,y); 107 int ans=Query(Root[x],Root[y],Root[lca],Root[f[lca][0]],1,num,k); 108 printf("%d\n",ans); lastans=ans; 109 } 110 else 111 { 112 scanf("%d%d",&x,&y); 113 x^=lastans; y^=lastans; 114 add(x,y); add(y,x); 115 if (Size[Top[x]]<Size[Top[y]]) DFS(x,y,Top[y]); 116 else DFS(y,x,Top[x]); 117 } 118 } 119 }