• 拓扑序计数


    拓扑序计数

      时间限制: 1 s

     空间限制: 128000 KB
    题目描述 Description

    求一颗有根树/树形图的拓扑序个数.

    输入描述 Input Description

    第一行一个n和一个素数P,表示这颗树有n个节点,要求拓扑序个数modP之后的结果.

    接下来n-1行,每行有两个数字x和y,表示x是y的父亲.

    保证1<=n<=1500000, n<P<2^31,P为质数.

    输出描述 Output Description

    一行一个数字,为该树形图拓扑序个数modP的结果.

    样例输入 Sample Input

    样例1
    4 100000007
    1 2
    1 3
    2 4

    样例2
    6 100000007
    1 2
    2 3
    1 4
    3 5
    5 6

    样例输出 Sample Output

    样例1
    3

    样例2
    5

    数据范围及提示 Data Size & Hint

    每个非根节点的儿子个数平均较多,数据量较大,建议c/c++选手才用scanf的读入方式

    题目链接:http://codevs.cn/problem/1304/


    树形DP,其实更像是树上计数。

    题目分析:题目要求的是一棵树上的拓扑序,树上的拓扑序肯定比图上的拓扑序有更好的性质。我们大致可以推出二叉树上子树合并到根的公式,然后再推广。

    状态设计:设ans1[x]为以结点x为根的子树的拓扑序个数,ans2[x]为以x为根的子树的节点个数。

    状态转移:当树为二叉树时,我们很容易发现ans2[u]=ans2[v1]+ans2[v2]+1,ans1[u]=C(ans2[v1]+ans2[v2],ans2[v1])*ans1[v1]*ans1[v2]。当树不为二叉树的时候,我们可以先把两个子树合并为一个子树,再把这个子树依次和其他子树合并。
    #include<bits/stdc++.h>
    #define N 3000055
    using namespace std;
    long long mod;
    long long jc[N],ny[N];
    
    long long qmod(long long x,long long y)
    {
        long long ans=1;
        while(y)
        {
            if(y%2==1)ans=(ans*x)%mod;
            y/=2;
            x=(x*x)%mod;
        }
        return ans;
    }
    
    void init()
    {
        jc[1]=1;
        for(int i=2;i<N;i++)jc[i]=(jc[i-1]*i)%mod;
        
        ny[N-1]=qmod(jc[N-1],mod-2);
        for(int i=N-2;i>=1;i--)ny[i]=(ny[i+1]*(i+1))%mod;
    }
    
    long long C(long long m,long long n)
    {
        if(n==0||m==n)return 1;
        return jc[m]*ny[n]%mod*ny[m-n]%mod;
    }
    
    struct ss
    {
        int v,next;
    };
    ss edg[N/2];
    int head[N/2],now_edge=0;
    int d[N/2]={0};
    
    void addedge(int u,int v)
    {
        edg[now_edge]=(ss){v,head[u]};
        head[u]=now_edge++;
    }
    
    long long ans1[N/2]={0},ans2[N/2]={0};
    
    void dfs(int x)
    {
        ans1[x]=1;
        
        for(int i=head[x];i!=-1;i=edg[i].next)
        {
            int v=edg[i].v;
            dfs(v);
            ans2[x]+=ans2[v];
            ans1[x]=C(ans2[x],ans2[v])*ans1[x]%mod*ans1[v]%mod;
        }
        ans2[x]++;
    }
    
    long long read()
    {
        long long ans=0;
        char ch=getchar();
        
        while(!(ch>='0'&&ch<='9'))ch=getchar();
        while(ch>='0'&&ch<='9')
        {
            ans*=10;
            ans+=ch-'0';
            ch=getchar();
        }
        return ans;
    }
    int main()
    {
        int n;
        //scanf("%d %lld",&n,&mod);
        n=(int)read();mod=read();
        
        init();
        memset(head,-1,sizeof(head));
        for(int i=1;i<n;i++)
        {
            int x,y;
        //    scanf("%d %d",&x,&y);
            x=read();y=read();
            addedge(x,y);
            d[y]++;
        }
        
        for(int i=1;i<=n;i++)
        if(!d[i])
        {
            dfs(i);
            printf("%lld
    ",ans1[i]);
            break;
        }
        
        return 0;
    }
    View Code
  • 相关阅读:
    QQ恢复解散后的群聊或删除后的好友的方法
    微软双屏手机Surface Duo曝新料
    利用Travis CI+GitHub实现持续集成和自动部署
    利用echarts展示旅行足迹
    03_K近邻算法
    02_感知机算法
    《面试官之你说我听》:简明的图解Redis RDB持久化、AOF持久化
    【绝对有收获】看看?必须告诉你为什么要使用MQ消息中间件(图解版)
    推荐收藏系列:一文理解JVM虚拟机(内存、垃圾回收、性能优化)解决面试中遇到问题(图解版)
    利用window.performance.timing进行性能分析
  • 原文地址:https://www.cnblogs.com/tian-luo/p/9741388.html
Copyright © 2020-2023  润新知