题目描述:戳这里
题解:
这个题目有一个非常好的技巧。我们要求的其实是某一个点在两颗树中的子树中同时含有的点的数量。我们发现这个东西很难求,因为这个东西在两个子树中都是散乱无序的。那么我们如果把一颗子树中的量变得有一定的关系可循,那么接下来只需要关心另一颗树就好了。那么我们不妨把第一颗树重新标一个号,编号就为原树的dfs序。那么第一颗子树中的点的编号都会变成连续的。接下来只要到第二颗子树中查询这个连续的区间中有多少点在某个子树中出现的次数就好了。这个可以用主席树来维护。
代码如下:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn=100005;
int n,tot,cnt,lnk[maxn],son[2*maxn],nxt[2*maxn],id[maxn],r[maxn],ll[maxn],rr[maxn],inn[maxn],inn1[maxn];
int lson[18*maxn],rson[18*maxn],c[18*maxn],root[maxn],que[maxn];
LL ans;
void add(int x,int y){son[++tot]=y,nxt[tot]=lnk[x],lnk[x]=tot;}
void dfs(int x){
id[x]=++cnt;
for (int j=lnk[x];j;j=nxt[j]) dfs(son[j]);
r[id[x]]=cnt;
}
void DFS(int x){
ll[x]=++cnt; que[cnt]=x;
for (int j=lnk[x];j;j=nxt[j]) DFS(son[j]);
rr[x]=cnt;
}
void upd(int x,int y){lson[x]=lson[y],rson[x]=rson[y],c[x]=c[y];}
void insert(int &x,int y,int l,int r,int num){
x=++tot; upd(x,y);
if (l==r) {c[x]++; return;}
int mid=(l+r)>>1;
if (num<=mid) insert(lson[x],lson[y],l,mid,num);
else insert(rson[x],rson[y],mid+1,r,num);
c[x]=c[lson[x]]+c[rson[x]];
}
int query(int x,int y,int ll,int rr,int l,int r){
if (ll<=l&&r<=rr) return c[y]-c[x];
int mid=(l+r)>>1,now=0;
if (ll<=mid) now+=query(lson[x],lson[y],ll,rr,l,mid);
if (rr>mid) now+=query(rson[x],rson[y],ll,rr,mid+1,r);
return now;
}
LL doit(int x){return 1ll*x*(x-1)/2;}
int main(){
scanf("%d",&n);
for (int i=1;i<n;i++){
int x,y; scanf("%d%d",&x,&y);
add(x,y); inn[y]++;
}
for (int i=1;i<=n;i++) if (inn[i]==0) {dfs(i); break;}
cnt=0; tot=0;
memset(lnk,0,sizeof(lnk));
for (int i=1;i<n;i++){
int x,y; scanf("%d%d",&x,&y);
add(id[x],id[y]); inn1[id[y]]++;
}
for (int i=1;i<=n;i++) if (inn1[i]==0) {DFS(i); break;}
tot=0;
for (int i=1;i<=n;i++)
insert(root[i],root[i-1],1,n,que[i]);
for (int i=1;i<=n;i++){
int x=id[i];
ans+=doit(query(root[ll[x]],root[rr[x]],x+1,r[x],1,n));
}
printf("%lld\n",ans);
return 0;
}