• 猴猴的比赛 dfs序


    猴猴的比赛 dfs序

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

    (nle 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;
    }
    
  • 相关阅读:
    父类与子类之间的调用顺序
    ROW_NUMBER() OVER函数的基本用法用法
    String类
    代码块
    权限修饰符
    内部类
    final&static
    面向对象思想
    oracle存储过程常用技巧
    ORACLE EXECUTE IMMEDIATE 用法
  • 原文地址:https://www.cnblogs.com/santiego/p/11777458.html
Copyright © 2020-2023  润新知