zoukankan      html  css  js  c++  java
  • 51nod1253 Kundu and Tree

    树包含N个点和N-1条边。树的边有2中颜色红色('r')和黑色('b')。给出这N-1条边的颜色,求有多少节点的三元组(a,b,c)满足:节点a到节点b、节点b到节点c、节点c到节点a的路径上,每条路径都至少有一条边是红色的。
    注意(a,b,c), (b,a,c)以及所有其他排列被认为是相同的三元组。输出结果对1000000007取余的结果。
     
     
    Input
    第1行:1个数N(1 <= N <= 50000)
    第2 - N行:每行2个数加一个颜色,表示边的起始点和结束的以及颜色。
    Output
    输出1个数,对应符合条件的3元组的数量。

    预处理对每条边(a,b),从a出发经过b的路径有多少条是经过/不经过红边的

    存在两种情况:

    1.a,b,c两两间路径不经过第三点,这时三条路径有唯一公共点,公共点到a,b,c的路径至少有两条有红边,由此可以统计

    2.a,b,c中有两点的路径经过第三点,这时枚举被经过的点进行统计

    #include<cstdio>
    typedef long long i64;
    const int N=50007,R=N*40;
    char buf[R+7],*ptr=buf-1;
    int _(){
        int x=0,c=*++ptr;
        while(c<48)c=*++ptr;
        while(c>47)x=x*10+c-48,c=*++ptr;
        return x;
    }
    int _c(){
        int c=*++ptr;
        while(c<'a')c=*++ptr;
        return c=='r';
    }
    int n;
    int es[N*2],enx[N*2],e0[N],ev[N*2],ep=2;
    int f1[N],f2[N],f3[N],sz[N];
    i64 ans=0;
    void dfs1(int w,int pa){
        sz[w]=1;
        for(int i=e0[w];i;i=enx[i]){
            int u=es[i];
            if(u!=pa){
                dfs1(u,w);
                sz[w]+=sz[u];
                if(ev[i])f1[w]+=f3[u]=sz[u];
                else f1[w]+=f3[u]=f1[u];
            }
        }
    }
    void dfs2(int w,int pa){
        for(int i=e0[w];i;i=enx[i]){
            int u=es[i];
            if(u!=pa){
                if(ev[i])f2[u]=n-sz[u];
                else f2[u]=f2[w]+f1[w]-f1[u];
                dfs2(u,w);
            }
        }
    }
    void dfs3(int w,int pa){
        static i64 fs[N],fl[N],fr[N],ss[N],sl[N],sr[N];
        static int p;
        for(int i=e0[w];i;i=enx[i]){
            int u=es[i];
            if(u!=pa)dfs3(u,w);
        }
        p=0;
        for(int i=e0[w];i;i=enx[i]){
            int u=es[i];
            ++p;
            if(u!=pa){
                fs[p]=fl[p]=fr[p]=f3[u];
                ss[p]=sl[p]=sr[p]=sz[u]-fs[p];
            }else{
                fs[p]=fl[p]=fr[p]=f2[w];
                ss[p]=sl[p]=sr[p]=n-sz[w]-fs[p];
            }
        }
        i64 a0=ans;
        for(int i=2;i<=p;++i)fl[i]+=fl[i-1],sl[i]+=sl[i-1];
        for(int i=p-1;i;--i)fr[i]+=fr[i+1],sr[i]+=sr[i+1];
        for(int i=2;i<p;++i){
            ans+=fl[i-1]*(fs[i]*sr[i+1]+ss[i]*fr[i+1]);
            ans+=(sl[i-1]+fl[i-1])*fs[i]*fr[i+1];
        }
        for(int i=1;i<p;++i)ans+=fl[i]*fs[i+1];
    }
    int main(){
        fread(buf,1,R,stdin);
        n=_();
        for(int i=1;i<n;++i){
            int a=_(),b=_(),c=_c();
            es[ep]=b;enx[ep]=e0[a];ev[ep]=c;e0[a]=ep++;
            es[ep]=a;enx[ep]=e0[b];ev[ep]=c;e0[b]=ep++;
        }
        dfs1(1,0);
        dfs2(1,0);
        dfs3(1,0);
        printf("%lld",ans%1000000007);
        return 0;
    }
  • 相关阅读:
    Intel x86
    FPGA自计数六位共阳极数码管动态显示2(调用task的方法)
    FPGA六位共阳极数码管动态显示
    运算放大器是模拟电路的基石,模拟电路设计
    这样讲你就懂了!大牛给你介绍《信号与系统》
    电容计算公式
    fork...join的用法
    芯片电源管脚的去耦电容究竟要用多大的?
    Blog Contents
    linux grep 命令常见用法
  • 原文地址:https://www.cnblogs.com/ccz181078/p/5858166.html
Copyright © 2011-2022 走看看