51NOD - 2553雙重祖先

題目鏈接: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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章