不错的题,思路很套路。考虑求的答案是
∑i,j∑k[k在两棵树中都是i,j的祖先]
改变枚举顺序,设 Son1(x) 表示第一棵树中以x为根的子树的点集,答案就变成:
∑k(|Son1(k)∩Son2(k)|2)
这个东西直接dfs序+树状数组。对一棵数建 dfs 序,另一棵 dfs 一趟,统计答案。
不需要主席树,每个节点只进行一次询问,可以算进入这个子树的之前和之后的差值,就是子树的答案。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100005,maxe=100005;
int n,dfn[maxn],out[maxn],dge[2][maxn],rt0,rt1;
int fir[2][maxn],nxt[2][maxe],son[2][maxe],tot[2];
LL ans;
void add(int k,int x,int y){
son[k][++tot[k]]=y; nxt[k][tot[k]]=fir[k][x]; fir[k][x]=tot[k];
}
int Tim;
void dfs0(int x){
dfn[x]=++Tim;
for(int j=fir[0][x];j;j=nxt[0][j]) dfs0(son[0][j]);
out[x]=Tim;
}
int bit[maxn];
void Update(int x,int val){
for(;x<=n;x+=(x&(-x))) bit[x]+=val;
}
int Query(int x){
int res=0;
for(;x;x-=(x&(-x))) res+=bit[x];
return res;
}
void dfs1(int x){
LL _t=Query(out[x])-Query(dfn[x]-1);
for(int j=fir[1][x];j;j=nxt[1][j]) dfs1(son[1][j]);
LL t=Query(out[x])-Query(dfn[x]-1)-_t; //printf("%d : %d\n",x,t);
ans+=t*(t-1)/2;
Update(dfn[x],1);
}
int main(){
freopen("51nod1681.in","r",stdin);
freopen("51nod1681.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n-1;i++){
int x,y; scanf("%d%d",&x,&y);
add(0,x,y); dge[0][y]++;
}
for(int i=1;i<=n-1;i++){
int x,y; scanf("%d%d",&x,&y);
add(1,x,y); dge[1][y]++;
}
for(int i=1;i<=n;i++) if(!dge[0][i]) rt0=i;
for(int i=1;i<=n;i++) if(!dge[1][i]) rt1=i;
dfs0(rt0); dfs1(rt1);
printf("%lld\n",ans);
return 0;
}