51nod:公共祖先(主席树 & DFS序)
2018-03-05 23:23
363 查看
有一个庞大的家族,共n人。已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边)。在另一个未知的平行宇宙,这n人的祖辈关系仍然是树形结构,但他们相互之间的关系却完全不同了,原来的祖先可能变成了后代,后代变成的同辈……两个人的亲密度定义为在这两个平行宇宙有多少人一直是他们的公共祖先。整个家族的亲密度定义为任意两个人亲密度的总和。Input
思路:转化为每个点在其子树有几个点相同。如果点是离散的就不好做了,所以通过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;
}
第一行一个数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 5Output示例
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;
}
相关文章推荐
- 51nod 1681 公共祖先 主席树dfs序
- 51nod 1681 公共祖先【树状数组】【DFS序】
- 51Nod 1681 公共祖先 [主席树做法]
- 【DFS序+树状数组】51Nod 1681 公共祖先
- hihoCoder 1062 最近公共祖先·一
- hihocoder 1067 最近公共祖先·二(tarjan LCA 离线算法O(n))
- 51Nod 1681 浅谈主席树套双DFS序
- hiho一下 第十五周 最近公共祖先·二 - 更新一下tarjan离线LCA模板
- hihocoder1069最近公共祖先·三(LCA在线算法--DFS+RMQ-ST)
- hihocoder 1067 最近公共祖先·二 并查集+stl
- 最近公共祖先LCA(Tarjan与DFS--ST倍增)
- hiho刷题日记——第十五最近公共祖先·二
- Timus 1329. Galactic History。LCA最近公共祖先或dfs递归离线处理!
- 51nod 1681 公共祖先 | 树状数组
- HDU 5927 Auxiliary Set 最近公共祖先 (dfs)
- 树中两个结点的最低公共祖先(超全解&&拓展)
- 最近公共祖先(LCA):离线&在线算法
- 牛客网-3 网易编程题(1拓扑&2二叉树的公共最近祖先&3快排找第K大数)
- hiho一下 第十七周 最近公共祖先·三 更新RMQ在线解LCA
- POJ - 1330 Nearest Common Ancestors(LCA最近公共祖先 朴素算法&倍增法)