• Permutation Counting (建图(深林图,有向图变树的特性条件)+树节点贡献问题(树型dp)+(组合数)) (MINIEYE杯十六届)


    题目:H-Permutation Counting_MINIEYE杯第十六届华中科技大学程序设计邀请赛 (nowcoder.com)

    思路:

    • 首先啊,先把题目读清楚, 给一对,(X,Y), 要满足Py>Px, 然后p(permutation )是一个1到n,且各个元素不同的数列
    • 问这个P啊有多少种排列方式
    • 首先 Py>Px , 代表着 位置 y是一定要大于 位置 x的, 利用这个可以建一个有向图,连接一个y到x的有向边
    • 什么时候符合条件? 就是没有环的时候符合条件
    • 其次 说有对(x,y) x都是不相同的, 相当于,每一个点最多只有1个父亲,于是这个性质就把图变成了树图(有向图,没有环,且父亲最多只有1个)
    • 注意这个树图是森林,没有要求是1个树.
    • 那么就转化为了,数的节点贡献问题, 该题关键就是就是他的size大小, 会拿size个数给他排大小去,对于他一定是比儿子大,他们之间的顺序就是固定的
    • 但是儿子之间的顺序没有固定,就变成了选球放盒子问题(每一个球不同),的一共种类数, 树与树之间也是如此 利用组合数解决
    • 具体看代码
    #include <bits/stdc++.h>
    using namespace std;
    #define ri register int 
    #define M  2000006
    
    vector <int> p[M];
    vector <int> q;
    
    int n,m;
    
    int vis[M];
    int sz[M];
    void dfs1(int a)
    {
        vis[a]=1;
        sz[a]++;
        for(ri i=0;i<p[a].size();i++)
        {
            int b=p[a][i];
        
            dfs1(b);
            sz[a]+=sz[b];
        }
        return ;
    }
    long long ans=1;
    const int mod=998244353;
    int in[M];
    long long arr[M],inv[M];
    long long ksn(long long a,int n)
    {
        long long ans=1;
        while(n)
        {
            if(n&1) ans=ans*a%mod;
            n>>=1;a=a*a%mod;
        }
        return ans;
    }
    void init(){
        
        arr[0]=1;inv[0]=1;
        for(ri i=1;i<=n;i++)
        {
            arr[i]=arr[i-1]*i%mod;
            inv[i]=ksn(arr[i],mod-2);
        }
    }
    long long zh(int a,int b)
    {
        if(a<b) return 0;
        if(a==b||b==0) return 1;
        return arr[a]*inv[a-b]%mod*inv[b]%mod;
    }
    void dfs2(int a)
    {
        int tmp=sz[a]-1;
        for(ri i=0;i<p[a].size();i++)
        {
            int b=p[a][i];
            ans=(ans*zh(tmp,sz[b]))%mod;
            tmp-=sz[b];
            dfs2(b);
        }
    }
    int main(){
        
        ios::sync_with_stdio(false);
        cin.tie(0);
        
        cin>>n>>m;
        init();
        for(ri i=1;i<=m;i++)
        {
            int a,b;
            cin>>a>>b;
            p[b].push_back(a);
            in[a]++;
        }
        for(ri i=1;i<=n;i++)
        {
            if(in[i]==0)
            {
                q.push_back(i);
            }
        }
        for(ri i=0;i<q.size();i++)
        {
            int b=q[i];
            dfs1(b);
        }
        for(ri i=1;i<=n;i++)
        {
            if(vis[i]==0)
            {
                printf("0\n");
                return 0;
            }
        }
        int tmp=n;
        for(ri i=0;i<q.size();i++)
        {
            int a=q[i];
            ans=(ans*zh(tmp,sz[a]))%mod;
            dfs2(a);
            tmp-=sz[a];
        }
        cout<<ans;
        return 0;
        
        
        
    } 
    View Code

    后记:

    • 也是通过2点之间的关系,从而连边,然后就可以建图
  • 相关阅读:
    Servlet的生命周期?
    C++图结构的图结构操作示例
    如何从google play下载app应用,直接下载apk
    C# Socket异步聊天例子
    三极管饱和,放大,截止电压判断
    java中的浮点(float)运算
    微软2014校园招聘笔试试题
    软件开发中的资源控制问题学习
    linux mount命令学习
    17、Spring Boot普通类调用bean【从零开始学Spring Boot】
  • 原文地址:https://www.cnblogs.com/Lamboofhome/p/16357366.html
Copyright © 2020-2023  润新知