1. 程式人生 > >P3302 [SDOI2013]森林(主席樹+倍增或LCT維護LCA)

P3302 [SDOI2013]森林(主席樹+倍增或LCT維護LCA)

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\n",ans); 119 } 120 } 121 return 0; 122 }

 

還有第二個思路,我們可以使用 $LCT$ 來維護 $LCA$,這裡還沒寫,下次再補