原文链接http://www.cnblogs.com/zhouzhendong/p/9016579.html
题目传送门 - Codechef EDGEST
题意
给定相同点集上的两棵生成树$T_1$和$T_2$,节点编号为$1$∼$N$。对于$T_1$中的每条边$e_1$,你需要求在$T_2$中有多少条边$e_2$满足:
• $T_1 − e_1 + e_2$(从$T_1$中删去$e_1$再加上$e_2$构成的图)是一棵生成树;
• $T_2 − e_2 + e_1$ 也是一棵生成树。
$1 ≤ T ≤ 10 , 2 ≤ N ≤ 2 imes 10^5 , ∑N ≤ 2 imes 10^5$
题解
首先,给这两棵无根树定根为$1$。
给$T_1$进行$dfs$,设“时间”表示当前已经访问的节点数,记节点$i$第一次访问的时间为$in_i$(即其$dfs$序)(要计入节点$i$),退出节点$i$的时间为$out_i$。
考虑删除$T_1$的一条边$y_1 ightarrow x_1$,其中$y_1$为$x_1$的父亲。
(1)则需要添加的边必定是$x_2$这棵子树中的节点对这棵子树外的节点的连边。即$forall ein T_2 , e=(x_2,y_2)$且($(in_{x_2}in [in_{x_1},out_{x_1}]且in_{y_2}in [1,in_{x_1})cup(out_{x_1},n])$或$(in_{y_2}in [in_{x_1},out_{x_1}]且in_{x_2}in [1,in_{x_1})cup(out_{x_1},n])$)。
考虑在$T_2$中,连接了$x_2$和$y_2$,则要删除的必定是$x_1$到$y_1$路径上的边。
其中,$x_1$到$y_1$的路径可以拆分成$x_1$到$T_2$根路径、$y_1$到根路径,以及两条$LCA_{T_2}(x_1,y_1)$到根的负路径。
在$T_1$中,我们要计算$x_1$这棵子树中的节点对这棵子树外的节点的连边,并且这个连边在$T_2$中存在。
于是梳理完上面的这些东西之后,我们可以开始说做法了。
我们用树套树维护(1)要弄的东西。
首先预处理在$T_1$中的每一对$(x_1,y_1)$需要查询的询问区间(即(1)中所指)、询问($T_2$中)哪一个节点$(x_1,y_1和LCA_{T_2}(x_1,y_1)$到根路径的贡献,把这些询问分别放到对应节点所准备的$vector$里面。
然后对$T2$进行$dfs$,对于$T_2$的每一个节点,每一个询问分别用当前树套树状态来查询,并在$dfs$的过程中修改树套树。
时间复杂度$O(nlog^2 n)$。但是常数较大,要$TLE$。
于是我们把树套树的第一维写成树状数组,再卡一波常数就可以通过了。
代码长到吐。
代码
#include <bits/stdc++.h> #pragma GCC optimize ("Ofast") #define time __time using namespace std; const int N=200005; int lowbit(int x){ return x&-x; } __inline char gc() { static char buf[300000], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 300000, stdin), p1 == p2) ? EOF : *p1++; } //#define gc getchar __inline int read(){ int x=0; char ch=gc(); while (!isdigit(ch)) ch=gc(); while (isdigit(ch)) x=(x<<1)+(x<<3)+ch-48,ch=gc(); return x; } int T,n,root[N],fa[N],in[N],out[N],time,ans[3][N]; int anst[N][20],depth[N]; struct pr{ int first,second; pr(){} pr(int a,int b){ first=a,second=b; } }; vector <pr> q[N]; struct Gragh{ int cnt,x[N*2],y[N*2],nxt[N*2],fst[N]; void clear(){ cnt=0; memset(fst,0,sizeof fst); } void add(int a,int b){ y[++cnt]=b,x[cnt]=a,nxt[cnt]=fst[a],fst[a]=cnt; } }a,b; struct Node{ int v,lc,rc; }t[N*800]; int STsize; __inline void update(int &rt,int L,int R,int x,int d){ if (!rt){ rt=++STsize; t[rt].v=t[rt].lc=t[rt].rc=0; } t[rt].v+=d; if (L==R) return; int mid=(L+R)>>1; if (x<=mid) update(t[rt].lc,L,mid,x,d); else update(t[rt].rc,mid+1,R,x,d); } __inline int query(int &rt,int L,int R,int xL,int xR){ if (!rt||L>xR||R<xL) return 0; if (xL<=L&&R<=xR) return t[rt].v; int mid=(L+R)>>1; if (xR<=mid) return query(t[rt].lc,L,mid,xL,xR); if (xL>mid) return query(t[rt].rc,mid+1,R,xL,xR); return query(t[rt].lc,L,mid,xL,mid)+query(t[rt].rc,mid+1,R,mid+1,xR); } __inline void update(int rt,int L,int R,int x,int y,int d){ for (int i=x;i<=n;i+=i&-i) update(root[i],1,n,y,d); } __inline int query(int rt,int L,int R,int xL,int xR,int yL,int yR){ int ans=0; for (int i=xR;i;i-=i&-i) ans+=query(root[i],1,n,yL,yR); for (int i=xL-1;i;i-=i&-i) ans-=query(root[i],1,n,yL,yR); return ans; } __inline void change(int x,int y,int d){ x=in[x],y=in[y]; if (x>y) swap(x,y); update(1,1,n,x,y,d); } __inline int ask(int x,int y){ int ans=0; if (1<=x-1) ans+=query(1,1,n,1,x-1,x,y); if (y+1<=n) ans+=query(1,1,n,x,y,y+1,n); return ans; } __inline void LCA_prepare(int x,int pre){ depth[x]=depth[pre]+1; anst[x][0]=pre; for (int i=1;i<20;i++) anst[x][i]=anst[anst[x][i-1]][i-1]; for (int i=b.fst[x];i;i=b.nxt[i]) if (b.y[i]!=pre) LCA_prepare(b.y[i],x); } __inline int LCA(int x,int y){ if (depth[x]<depth[y]) swap(x,y); for (int i=19;i>=0;i--) if (depth[anst[x][i]]>=depth[y]) x=anst[x][i]; if (x==y) return x; for (int i=19;i>=0;i--) if (anst[x][i]!=anst[y][i]) x=anst[x][i],y=anst[y][i]; return anst[x][0]; } __inline void dfsa(int x,int pre){ fa[x]=pre; in[x]=++time; if (pre){ q[x].push_back(pr(x,0)); q[pre].push_back(pr(x,1)); q[LCA(x,pre)].push_back(pr(x,2)); } for (int i=a.fst[x];i;i=a.nxt[i]) if (a.y[i]!=pre) dfsa(a.y[i],x); out[x]=time; } __inline void dfsb(int x,int pre){ if (pre) change(x,pre,1); /* for (int i=0;i<q[x].size();i++){ int y=q[x][i].first,t=q[x][i].second; ans[t][y]=ask(in[y],out[y]); }*/ while (!q[x].empty()){ int y=q[x].back().first,t=q[x].back().second; ans[t][y]=ask(in[y],out[y]); q[x].pop_back(); } for (int i=b.fst[x];i;i=b.nxt[i]) if (b.y[i]!=pre) dfsb(b.y[i],x); if (pre) change(x,pre,-1); } int main(){ T=read(); while (T--){ n=read(); STsize=0; memset(root,0,sizeof root); a.clear(),b.clear(); for (int i=1,x,y;i<n;i++){ x=read(),y=read(); a.add(x,y),a.add(y,x); } for (int i=1,x,y;i<n;i++){ x=read(),y=read(); b.add(x,y),b.add(y,x); } for (int i=0;i<=n;i++) for (int j=0;j<20;j++) anst[i][j]=0; LCA_prepare(1,0); for (int i=1;i<=n;i++) q[i].clear(); time=0; dfsa(1,0); dfsb(1,0); for (int i=1;i<=(n-1)*2;i+=2){ int x=a.x[i],y=a.y[i]; if (fa[y]==x) swap(x,y); printf("%d ",ans[0][x]+ans[1][x]-2*ans[2][x]); } puts(""); } return 0; }