题目链接:51NOD - 2553双重祖先
先对第一颗树处理,求出每个点的dfs序。
然后对于第二课树,我们就只需要在子树中找当前点在第一颗树的dfs的区间中的点的个数。
子树中直接线段树合并即可。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e5+10,M=N*40;
int n,rt[N],sum[M],lc[M],rc[M],cnt; long long res;
int st[N],ed[N],idx;
vector<int> g1[N],g2[N];
void change(int &p,int l,int r,int x){
if(!p) p=++cnt;
if(l==r){sum[p]++; return ;}
int mid=l+r>>1;
if(x<=mid) change(lc[p],l,mid,x);
else change(rc[p],mid+1,r,x);
sum[p]=sum[lc[p]]+sum[rc[p]];
}
int ask(int p,int l,int r,int ql,int qr){
if(!p) return 0;
if(l==ql&&r==qr) return sum[p];
int mid=l+r>>1;
if(qr<=mid) return ask(lc[p],l,mid,ql,qr);
else if(ql>mid) return ask(rc[p],mid+1,r,ql,qr);
else return ask(lc[p],l,mid,ql,mid)+ask(rc[p],mid+1,r,mid+1,qr);
}
int merge(int x,int y,int l,int r){
if(!x||!y) return x^y;
if(l==r){sum[x]+=sum[y]; return x;}
int mid=l+r>>1;
lc[x]=merge(lc[x],lc[y],l,mid);
rc[x]=merge(rc[x],rc[y],mid+1,r);
sum[x]=sum[lc[x]]+sum[rc[x]];
return x;
}
void dfs1(int x,int fa){
st[x]=++idx;
for(auto to:g1[x]) if(to!=fa) dfs1(to,x);
ed[x]=idx;
}
void dfs2(int x,int fa){
for(auto to:g2[x]) if(to!=fa){
dfs2(to,x); rt[x]=merge(rt[x],rt[to],1,n);
}
res+=ask(rt[x],1,n,st[x],ed[x]);
change(rt[x],1,n,st[x]);
}
signed main(){
cin>>n;
for(int i=1,a,b;i<n;i++)
scanf("%d %d",&a,&b),g1[a].push_back(b),g1[b].push_back(a);
for(int i=1,a,b;i<n;i++)
scanf("%d %d",&a,&b),g2[a].push_back(b),g2[b].push_back(a);
dfs1(1,1); dfs2(1,1);
cout<<res;
return 0;
}