• 拓扑序计数


    拓扑序计数

      时间限制: 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
  • 相关阅读:
    基础总结深入:数据类型的分类和判断(数据、内存、变量) 对象 函数 回调函数 IIFE 函数中的this 分号
    BOM 定时器 通过修改元素的类来改变css JSON
    事件 事件的冒泡 事件的委派 事件的绑定 事件的传播
    DOM修改 使用DOM操作CSS
    包装类 Date Math 字符串的相关的方法 正则表达式 DOM DOM查询
    数组 call()、apply()、bind()的使用 this arguments
    autocad 二次开发 最小包围圆算法
    win10 objectarx向导在 vs2015中不起作用的解决办法
    AutoCad 二次开发 jig操作之标注跟随线移动
    AutoCad 二次开发 文字镜像
  • 原文地址:https://www.cnblogs.com/tian-luo/p/9741388.html
Copyright © 2020-2023  润新知