P3302 [SDOI2013]森林(主席树+倍增或LCT维护LCA)
这道题要我们维护区间第K大,我们想到了主席树。
而这道题要我们动态维护加边,我们想到了 $LCT$ 。
对于树上的一条路径,我们可以使用差分的思想,设 $x$ 到 $y$ 的路径, $x$ 与 $y$ 的最近公共祖先为 $lca$ ,$A_i$ 表示从 $i$点到根结点维护的信息,那么我们可以利用 $A_x + A_y - A_{lca} - A_{fa_{lca}}$ 处理处$x$ 到 $y$ 的路径路径上的信息。
我们可以使用启发式合并来在线维护两个主席树的合并,和线段树合并差不多,将 $size$ 小的并向 $size$ 大的。
再考虑怎么维护 $lca$ ,我们考虑使用倍增求 $lca$ 那么我们需要做的是在每次主席树合并时更新倍增数组,我们只需在 $dfs$ 时稍作修改。
$update: $一定要写对启发式合并不要把 $fa[x]$ 与 $x$ 混淆。
1 #include<bits/stdc++.h> 2 #define MAXN 80001 3 using namespace std; 4 inline int read () 5 { 6 int s=0,w=1; 7 char ch=getchar (); 8 while (ch<'0'||ch>'9'){if (ch=='-') w=-1;ch=getchar ();} 9 while ('0'<=ch&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar (); 10 return s*w; 11 } 12 struct edge{ 13 int v,nxt; 14 }e[MAXN<<2]; 15 struct President_Tree{ 16 int l,r,size; 17 }tr[MAXN*500]; 18 int testcase,n,m,t,Max,len,cnt,ans; 19 int head[MAXN],a[MAXN],root[MAXN],fa[MAXN],size[MAXN]; 20 int mi[17],f[MAXN][17],dep[MAXN]; 21 vector<int>Vec; 22 bool used[MAXN]; 23 inline void add (int u,int v) 24 { 25 e[++cnt].v=v,e[cnt].nxt=head[u],head[u]=cnt; 26 } 27 inline int find (int x) 28 { 29 if (fa[x]!=x) fa[x]=find (fa[x]); 30 return fa[x]; 31 } 32 inline void build (int &x,int l,int r) 33 { 34 tr[x=++len].size=0; 35 if (l==r) return; 36 int mid=(l+r)>>1; 37 build (tr[x].l,l,mid); 38 build (tr[x].r,mid+1,r); 39 } 40 inline void update (int &x,int y,int l,int r,int pos) 41 { 42 tr[x=++len]=tr[y]; 43 tr[x].size++; 44 if (l==r) return; 45 int mid=(l+r)>>1; 46 if (pos<=mid) update (tr[x].l,tr[y].l,l,mid,pos); 47 else update (tr[x].r,tr[y].r,mid+1,r,pos); 48 } 49 inline void dfs (int u,int ff,int rt) 50 { 51 fa[u]=ff;size[rt]++; 52 used[u]=1,dep[u]=dep[ff]+1,f[u][0]=ff; 53 for (int i=1;i<=16;i++) f[u][i]=f[f[u][i-1]][i-1]; 54 update (root[u],root[ff],1,Max,a[u]); 55 for (register int i=head[u];i!=0;i=e[i].nxt) 56 if (e[i].v!=ff) 57 dfs (e[i].v,u,rt); 58 } 59 inline void merge (int x,int y) 60 { 61 int r1=find (x),r2=find (y); 62 if (size[r1]<size[r2]) swap (x,y),swap (r1,r2); 63 add (x,y),add (y,x); 64 dfs (y,x,r1); 65 } 66 inline int LCA (int x,int y) 67 { 68 if (x==y) return x; 69 if (dep[x]<dep[y]) swap (x,y); 70 for (register int i=16;i>=0;i--) 71 if (dep[f[x][i]]>=dep[y]) 72 x=f[x][i]; 73 if (x==y) return x; 74 for (register int i=16;i>=0;i--) 75 if (f[x][i]!=f[y][i]) 76 x=f[x][i],y=f[y][i]; 77 return f[x][0]; 78 } 79 inline int query (int x,int y,int pre1,int pre2,int l,int r,int k) 80 { 81 if (l==r) return Vec[l-1]; 82 int lsize=tr[tr[x].l].size+tr[tr[y].l].size-tr[tr[pre1].l].size-tr[tr[pre2].l].size; 83 int mid=(l+r)>>1; 84 if (k<=lsize) return query (tr[x].l,tr[y].l,tr[pre1].l,tr[pre2].l,l,mid,k); 85 else return query (tr[x].r,tr[y].r,tr[pre1].r,tr[pre2].r,mid+1,r,k-lsize); 86 } 87 int main() 88 { 89 mi[0]=1;for (register int i=1;i<=16;i++) mi[i]=mi[i-1]<<1; 90 testcase=read (); 91 n=read (),m=read (),t=read (); 92 for (register int i=1;i<=n;i++) fa[i]=i; 93 for (register int i=1;i<=n;i++) a[i]=read (),Vec.push_back (a[i]); 94 sort (Vec.begin (),Vec.end ()); 95 Vec.erase (unique (Vec.begin (),Vec.end ()),Vec.end ()); 96 for (register int i=1;i<=n;i++) 97 a[i]=lower_bound (Vec.begin (),Vec.end (),a[i])-Vec.begin ()+1; 98 Max=Vec.size (); 99 for (register int i=1;i<=m;i++) 100 { 101 int u=read (),v=read (); 102 add (u,v),add (v,u); 103 } 104 build (root[0],1,Max); 105 for (register int i=1;i<=n;i++) 106 if (!used[i]) 107 dfs (i,0,i); 108 while (t--) 109 { 110 char ch=getchar (); 111 while (ch!='L'&&ch!='Q') ch=getchar (); 112 int x=read ()^ans,y=read ()^ans; 113 if (ch=='L') merge (x,y); 114 else 115 { 116 int k=read ()^ans,lca=LCA (x,y); 117 ans=query (root[x],root[y],root[lca],root[f[lca][0]],1,Max,k); 118 printf ("%d ",ans); 119 } 120 } 121 return 0; 122 }
还有第二个思路,我们可以使用 $LCT$ 来维护 $LCA$,这里还没写,下次再补