您的位置:首页 > 其它

51nod:公共祖先(主席树 & DFS序)

2018-03-05 23:23 363 查看
有一个庞大的家族,共n人。已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边)。在另一个未知的平行宇宙,这n人的祖辈关系仍然是树形结构,但他们相互之间的关系却完全不同了,原来的祖先可能变成了后代,后代变成的同辈……两个人的亲密度定义为在这两个平行宇宙有多少人一直是他们的公共祖先。整个家族的亲密度定义为任意两个人亲密度的总和。Input
第一行一个数n(1<=n<=100000)
接下来n-1行每行两个数x,y表示在第一个平行宇宙x是y的父亲。
接下来n-1行每行两个数x,y表示在第二个平行宇宙x是y的父亲。
Output
一个数,表示整个家族的亲密度。
Input示例
5
1 3
3 5
5 4
4 2
1 2
1 3
3 4
1 5
Output示例
6
题意:给两棵树,问所有两个点的亲密度之和是多少,两个点的亲密度即他们在两棵树中的公共祖先个数。
思路:转化为每个点在其子树有几个点相同。如果点是离散的就不好做了,所以通过dfs序将子树变成连续的编号,就可以通过“树状数组”或者“主席树”来统计子树有几个相同的点了。这里练习一下主席树。# include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+30;
struct node{int l, r, sum;}T[maxn*30];//开小了会T
vector<int>g[maxn];
int root[maxn], in[maxn],out[maxn], in2[maxn], tot=0, n, cnt=0;
int du[maxn];
long long ans = 0;
inline void scan(int &ret)
{
char c; ret = 0;
while ((c = getchar())<'0'||c>'9');
while(c>='0'&&c<='9') ret = ret*10+(c-'0'), c = getchar();
}
inline void Out(long long x)
{
if(x/10) Out(x/10);
putchar('0'+x%10);
}
void update(int l, int r, int &x, int y, int val)
{
T[++cnt] = T[y], ++T[cnt].sum, x=cnt;
if(l==r) return;
int mid=l+r>>1;
if(val<=mid) update(l, mid, T[x].l, T[y].l, val);
else update(mid+1, r, T[x].r, T[y].r, val);
}
int query(int l, int r, int ll, int rr, int x, int y)
{
if(ll<=l && rr>=r) return T[x].sum-T[y].sum;
int mid=l+r>>1, tmp=0;
if(ll<=mid) tmp += query(l, mid, ll, rr, T[x].l, T[y].l);
if(rr>mid) tmp += query(mid+1, r, ll, rr, T[x].r, T[y].r);
return tmp;
}
void dfs(int cur)
{
in[cur]=++tot;
for(int to:g[cur]) dfs(to);
out[cur]=tot;
}
void dfs2(int cur, int fa)
{
in2[cur]=++tot;
update(1, n, root[tot], root[fa], in[cur]);
for(int to:g[cur]) dfs2(to, tot);
if(in[cur]==out[cur] || tot==in2[cur]) return;
int tmp = query(1, n, in[cur]+1, out[cur], root[tot], root[in2[cur]]);
ans += (long long)tmp*(tmp-1)/2;
}
int main()
{
int x, y, base;
scan(n);
for(int i=1; i<n; ++i)
{
scan(x), scan(y);
g[x].push_back(y);
++du[y];
}
for(int i=1; i<=n; ++i) if(du[i]==0){base=i; break;}
dfs(base); tot = 0;
for(int i=1; i<=n; ++i) g[i].clear(), du[i]=0;
for(int i=1; i<n; ++i)
{
scan(x), scan(y);
g[x].push_back(y);
++du[y];
}
for(int i=1; i<=n; ++i) if(du[i]==0){base=i; break;}
dfs2(base, 0);
Out(ans);
putchar('\n');
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: