码迷,mamicode.com
首页 > 其他好文 > 详细

猴猴的比赛 dfs序

时间:2019-11-01 16:47:04      阅读:78      评论:0      收藏:0      [点我收藏+]

标签:head   efi   枚举   stdout   开始   turn   open   main   pac   

猴猴的比赛 dfs序

两颗\(n\)节点的树,不相同,问多少点对\((u,v)\)在两棵树上均满足路径\(v\)\(u\)子树中

\(n\le 10^5\)

暴力:

\(n^2\)暴力枚举点对用\(dfs\)\(O(1)\)判断是非满足条件,或者用欧拉序\(O(1)\)求lca

正解:

先跑第一棵树,求出其\(dfs\)序,记录下节点\(i\)\(dfs\)序开始与结束位置。

然后跑第二棵树,维护一个下标为\(dfs\)序的树状数组,每次第一次遍历到节点\(i\)时,我们统计在当前节点的\(dfs\)序之前(即满足在第一棵树上节点\(i\)\(j\)的子树中)且在当前这第二棵树上已经遍历过的节点(即满足在第二棵树上节点\(i\)\(j\)的子树中)的个数,加入到答案。这个过程相当于统计每个\((u,v)\)中的\(v\)

具体看代码实现吧。

#include <cstdio>
#define MAXN 100001
using namespace std;
inline int read(){
    char ch=getchar();int s=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') s=s*10+(ch^'0'), ch=getchar();
    return s;
}
int n;
int tre[MAXN];
void add(int x, int val){
    while(x<=n)
        tre[x]+=val,x+=x&(-x);
}
int get_sum(int x){
    int res=0;
    while(x>0)
        res+=tre[x],x-=x&(-x);
    return res;
}
int dfn[MAXN],dfn_out[MAXN],cnt;
int ans[MAXN];
namespace tre1 {
    int head[MAXN],nxt[MAXN*2],vv[MAXN*2],tot;
    inline void add_edge(int u, int v){
        vv[++tot]=v;
        nxt[tot]=head[u];
        head[u]=tot;
    }
    void dfs(int u, int fa){
        dfn[u]=++cnt;
        for(int i=head[u];i;i=nxt[i]){
            int v=vv[i];
            if(v==fa) continue;
            dfs(v, u);
        }
        dfn_out[u]=cnt;
    }
}
namespace tre2 {
    int head[MAXN],nxt[MAXN*2],vv[MAXN*2],tot;
    inline void add_edge(int u, int v){
        vv[++tot]=v;
        nxt[tot]=head[u];
        head[u]=tot;
    }
    void solve(int u, int fa){
        ans[u]=get_sum(dfn[u]-1);
        add(dfn[u], 1);
        add(dfn_out[u], -1);
        for(int i=head[u];i;i=nxt[i]){
            int v=vv[i];
            if(v==fa) continue;
            solve(v, u);
        }
        add(dfn[u], -1);
        add(dfn_out[u], 1);
    }
}
int main(){
    //freopen("climb.in", "r", stdin);
    //freopen("climb.out", "w", stdout);
    n=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        tre1::add_edge(u, v);
        tre1::add_edge(v, u);
    }
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        tre2::add_edge(u, v);
        tre2::add_edge(v, u);
    }
    tre1::dfs(1, 1);
    tre2::solve(1, 1);
    long long sum=0;
    for(int i=1;i<=n;++i) sum+=ans[i];
    printf("%lld", sum);
    return 0;
}

猴猴的比赛 dfs序

标签:head   efi   枚举   stdout   开始   turn   open   main   pac   

原文地址:https://www.cnblogs.com/santiego/p/11777458.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!