zoukankan      html  css  js  c++  java
  • 题解 第一计数

    题目描述

    众所周知(mathcal{Ame})__喜欢打牌,尤其喜欢玩(szb),但是这题和打牌没什么关系。

    此为第一计数。

    (n) 个球和 (m) 个盒子,要全部装进盒子里。
    还有一些限制条件,那么有多少种方法放球?(与放的先后顺序无关)

    限制条件如下:

    ( ext{I}):球之间互不相同,盒子之间互不相同。

    ( ext{II}):球之间互不相同,盒子之间互不相同,每个盒子至多装一个球。

    ( ext{III}):球之间互不相同,盒子之间互不相同,每个盒子至少装一个球。

    ( ext{IV}):球之间互不相同,盒子全部相同。

    ( ext{V}):球之间互不相同,盒子全部相同,每个盒子至多装一个球。

    ( ext{VI}):球之间互不相同,盒子全部相同,每个盒子至少装一个球。

    ( ext{VII}):球全部相同,盒子之间互不相同。

    ( ext{VIII}):球全部相同,盒子之间互不相同,每个盒子至多装一个球。

    ( ext{IX}):球全部相同,盒子之间互不相同,每个盒子至少装一个球。

    ( ext{X}):球全部相同,盒子全部相同。

    ( ext{XI}):球全部相同,盒子全部相同,每个盒子至多装一个球。

    ( ext{XII}):球全部相同,盒子全部相同,每个盒子至少装一个球。

    由于答案可能很大,所以要对 (998244353) 取模。

    输入格式

    输入一行,两个数字,分别为(n)(m)

    输出格式

    输出十二行,每行一个整数,对应每一种限制条件的答案。

    样例输入:

    13 6

    样例输出:

    83517427
    0
    721878522
    19628064
    0
    9321312
    8568
    0
    792
    71
    0
    14

    数据范围与提示

    对于(50\%)数据,(n,mle 1000)

    对于(100\%)数据,(n,mle 10000)

    解题思路

    组合数学入门题大合集对于(12)种情况讨论即可

    ( ext{I}):球之间互不相同,盒子之间互不相同。

    显然的我们有总方案数为(m^n)

    ( ext{II}):球之间互不相同,盒子之间互不相同,每个盒子至多装一个球。

    考虑每次放进一个盒子之后都减一,最后为(m imes (m-1) imes (m-2)..... imes (m-n+1)=m^{underline{n}})

    ( ext{III}):球之间互不相同,盒子之间互不相同,每个盒子至少装一个球。

    考虑第二类斯特林数,对于(egin{Bmatrix}n\mend{Bmatrix})的意义为将有(n)个物品的集合划分到(m)个非空集合,可以看做盒子相同的情况,乘上(m!)为盒子不同的情况

    同时也可以考虑容斥:枚举多少个盒子空了,然后剩下的部分就是第一个部分了。然后就可以得到下面这个式子:

    [sum_{i=0}^{m} (-1)^{i} inom{m}{i} (m-i)^{n} ]

    ( ext{IV}):球之间互不相同,盒子全部相同。

    用第二类斯特林数枚举装了多少个球,即为

    [sum_{i=1}^{m} egin{Bmatrix}n\iend{Bmatrix} ]

    ( ext{V}):球之间互不相同,盒子全部相同,每个盒子至多装一个球。

    无论球在哪个盒子都是一样的,即为

    [[nle m] ]

    ( ext{VI}):球之间互不相同,盒子全部相同,每个盒子至少装一个球。

    显然直接第二类斯特林数

    [egin{Bmatrix}n\mend{Bmatrix} ]

    或者二项式反演

    [ans_6=S_2(n,m)=frac{1}{m!}ans_3=frac{1}{m!}sum_{i=0}^m(-1)^iinom{m}{i}(m-i)^n ]

    此为第二类斯特林数通项公式。

    ( ext{VII}):球全部相同,盒子之间互不相同。

    隔板法即可,最后答案为

    [inom{n+m-1}{m-1} ]

    ( ext{VIII}):球全部相同,盒子之间互不相同,每个盒子至多装一个球。

    相当于选(n)个盒子装球,方案为

    [inom{m}{n} ]

    ( ext{IX}):球全部相同,盒子之间互不相同,每个盒子至少装一个球。

    同样利用插板法,我们先钦定每个盒子里头放一个球,剩下 (n-m) 个球按照第七个做就行了。

    [inom{n-1}{m-1} ]

    ( ext{X}):球全部相同,盒子全部相同。

    (dp_{n,m})(n)个球 分到 (m) 个相同的方案数。那么我们要的答案就是 (dp_{n,m})

    显然有一个(n^2)(dp)转移柿子为

    [dp_{i,j} = dp_{i-j,j} + dp_{i,j-1} ]

    两者的意思分别为:对于有空盒子的情况去掉一个空盒子(等号右边),无空盒子则从每个盒子里减(1)个球。

    可以对其构造生成函数,

    [F_i(x)=dp_{0,i}+dp_{1,i}x+dp_{2,i}x^{2}+dp_{3,i}x^{3} dots ]

    然后不难发现

    [F_i(x) = F_{i-1}(x) imes (1+x^i+x^{2i}+x^{3i}dots) ]

    [F_i(x) = frac{F_{i-1}(x)}{1-x^{i}} ]

    于是就有

    [F_{i}(x) = prod_{j=1}^{i} frac{1}{1-x^{j}} ]

    直接暴力多项式(exp)即可做到(O(nlogn))

    ( ext{XI}):球全部相同,盒子全部相同,每个盒子至多装一个球。

    和第五个是一样的东西

    [[nleq m] ]

    ( ext{XII}):球全部相同,盒子全部相同,每个盒子至少装一个球。

    我们强制现在每个盒子里头放一个球,就是和( ext{X})同样的问题了。答案即为 (dp_{n-m,m})

    #include<bits/stdc++.h>
        
    #define LL long long
        
    #define _ 0
        
    #define R register
    
    using namespace std;
        
    /*Grievous Lady*/
        
    template <typename _n_> void mian(_n_ & _x_){
        _x_ = 0;int _m_ = 1;char buf_ = getchar();
        while(buf_ < '0' || buf_ > '9'){if(buf_ == '-')_m_ =- 1;buf_ = getchar();}
        do{_x_ = (_x_ << 1) + (_x_ << 3) + buf_ - '0';buf_ = getchar();}while(buf_ >= '0' && buf_ <= '9');_x_ *= _m_;
    }
    
    // #define int long long
    
    #define mod 998244353
    
    const int kato = 1e6 + 10;
    
    int n , m , len;
    
    int fac[kato] , inv[kato] , fac_inv[kato] , A[kato] , B[kato] , S[kato] , F[kato];
    
    #define init() 
    { 
        for(len = 1;len <= (max(n , m) << 1);len <<= 1); 
        fac[0] = fac[1] = inv[0] = inv[1] = fac_inv[0] = fac_inv[1] = 1; 
        for(R int i = 2;i <= n + m;i ++){ 
            fac[i] = 1LL * fac[i - 1] * i % mod; 
            inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod; 
            fac_inv[i] = 1LL * fac_inv[i - 1] * inv[i] % mod; 
        } 
        for(R int i = 0;i <= n;i ++){ 
            A[i] = (i & 1) ? mod - fac_inv[i] : fac_inv[i]; 
            B[i] = fac_inv[i] * 1LL * quick_pow(i , n) % mod; 
        } 
    }
    
    inline int quick_pow(int a , int b){
        R int res = 1;
        for(; b ; b >>= 1 , a = 1LL * a * a % mod){
            if(b & 1){
                res = 1LL * res * a % mod;
            }
        }
        return res;
    }
    
    inline int c(int a , int b){
        if(a < b) return 0;
        return 1LL * fac[a] * fac_inv[b] % mod * fac_inv[a - b] % mod; 
    }
    
    inline void NTT(int *y , int len , int opt){
        R int *rev = new int[len];
        rev[0] = 0;
        for(R int i = 1;i < len;i ++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (len >> 1));
        for(R int i = 0;i < len;i ++) if(i < rev[i]) swap(y[i] , y[rev[i]]);
        for(R int i = 1;i < len;i <<= 1){
            R int g1 = quick_pow(3 , (mod - 1) / (i << 1));
            for(R int j = 0;j < len;j += (i << 1)){
                for(R int k = 0 , g = 1;k < i;k ++ , g = g * 1LL * g1 % mod){
                    R int res = y[i + k + j] * 1LL * g % mod;
                    y[i + k + j] = ((y[j + k] - res) % mod + mod) % mod;
                    y[j + k] = (y[j + k] + res) % mod;
                }
            }
        }
        if(opt == -1){
            reverse(y + 1 , y + len);
            for(R int i = 0 , inv = quick_pow(len , mod - 2);i < len;i ++) y[i] = y[i] * 1LL * inv % mod;
        }
        delete []rev;
    }
    
    void poly_inv(int *a , int len){
        if(len == 1){ a[0] = quick_pow(a[0] , mod - 2); return;}
        R int len1 = len / 2;
        R int *g = new int[len * 2];
        for(R int i = 0;i < len1;i ++) g[i] = a[i];
        for(R int i = len1;i < len * 2;i ++) g[i] = 0;
        poly_inv(g , len1);
        for(R int i = len1;i < len * 2;i ++) g[i] = 0;
        NTT(g , len * 2 , 1) , NTT(a , len * 2 , 1);
        for(R int i = 0;i < len * 2;i ++) a[i] = ((2 * g[i] % mod - a[i] * 1LL * g[i] % mod * g[i] % mod) % mod + mod) % mod;
        NTT(a , len * 2 , -1);
        for(R int i = len;i < len * 2;i ++) a[i] = 0;
        delete []g;
    }
    
    inline void poly_dev(int *a , int len){
        for(R int i = 1;i < len;i ++) a[i - 1] = a[i] * 1LL * i % mod; 
        a[len - 1] = 0;
    }
    
    inline void poly_dev_inv(int *a , int len){
        for(R int i = len + 1 ; i ; i --) a[i] = a[i - 1] * 1LL * inv[i] % mod;
        a[0] = 0;
    }
    
    inline void get_ln(int *a, int len){
        R int *b = new int[len * 2];
        for(R int i = 0;i < len;i ++) b[i] = a[i];
        for(R int i = len;i < len << 1;i ++) b[i] = 0;
        poly_dev(b , len); poly_inv(a , len);
        NTT(a , len << 1 , 1); NTT(b , len << 1 , 1);
        for(R int i = 0;i < len << 1;i ++) a[i] = a[i] * 1LL * b[i] % mod;
        NTT(a , len << 1 , -1);
        poly_dev_inv(a , len);
        for(R int i = len;i < len << 1;i ++) a[i] = 0;
        delete []b;
    }
    
    
    void poly_exp(int *a , int len){
        if(len == 1) { a[0] ++; return; }
        int len1 = len / 2;
        int *g = new int[len << 1];
        for(int i = 0;i < len1;i ++) g[i] = a[i];
        for(int i = len1;i < len << 1;i ++) g[i] = 0;
        poly_exp(g , len1);
        for(int i = len1;i < len << 1;i ++) g[i] = 0;
        int *lng = new int[len << 1];
        for(int i = 0;i < len << 1;i ++) lng[i] = g[i];
        get_ln(lng , len); a[0] ++;
        for(int i = 0;i < len;i ++){ a[i] -= lng[i]; if (a[i] < 0) a[i] += mod; }
        NTT(a , len << 1 , 1); NTT(g , len << 1 , 1);
        for(int i = 0;i < len << 1;i ++) a[i] = a[i] * 1LL * g[i] % mod;
        NTT(a , len << 1 , -1);
        for(int i = len;i < len << 1;i ++) a[i] = 0;
        delete []g;
    }
    
    inline int solve_I(){
        return quick_pow(m , n);
    }
    
    inline int solve_II(){
        R int res = 1;
        for(R int i = m ; i >= m - n + 1 ; i --){
            res = 1LL * res * i % mod;
        }
        return res;
    }
    
    inline int solve_III(){
        return 1LL * S[m] * fac[m] % mod;
    }
    
    inline int solve_IV(){
        int res = 0;
        for(R int i = 1;i <= m;i ++){
            res = (res + S[i]) % mod;
        }
        return res % mod;
    }
    
    inline int solve_V(){
        return n > m ? 0 : 1;
    }
    
    inline int solve_VI(){ 
        return n < m ? 0 : S[m]; 
    }
    
    inline int solve_VII(){
        return c(n + m - 1 , m - 1);
    }
    
    inline int solve_VIII(){
        return c(m , n);
    }
    
    inline int solve_IX(){
        return c(n - 1 , m - 1);
    }
    
    inline int solve_X(){
        return F[n];
    }
    
    inline int solve_XI(){
        return n > m ? 0 : 1;
    }
    
    inline int solve_XII(){
        return n < m ? 0 : F[n - m];
    }
    
    inline int Ame_(){
        mian(n) , mian(m); init();
        NTT(A , len , 1); NTT(B , len , 1);
        for(R int i = 0;i < len;i ++) A[i] = 1LL * A[i] * B[i] % mod;
        NTT(A , len , -1);
        for(R int i = 0;i <= n;i ++) S[i] = A[i];
        for(R int i = 1;i <= m;i ++){
            for(R int j = i;j <= n;j += i){
                F[j] = (F[j] + inv[j / i]) % mod;
            }
        }
        poly_exp(F , len / 2); 
        printf("%d
    " , solve_I());
        printf("%d
    " , solve_II());
        printf("%d
    " , solve_III());
        printf("%d
    " , solve_IV());
        printf("%d
    " , solve_V());
        printf("%d
    " , solve_VI());
        printf("%d
    " , solve_VII());
        printf("%d
    " , solve_VIII());
        printf("%d
    " , solve_IX());
        printf("%d
    " , solve_X());
        printf("%d
    " , solve_XI());
        printf("%d
    " , solve_XII());
        fclose(stdin); fclose(stdout);
        return ~~(0^_^0);
    }
        
    int Ame__ = Ame_();
        
    signed main(){;}
    
  • 相关阅读:
    八数码问题
    Choose and divide
    紫书 动态规划例题
    3657 括号序列 动态规划
    动态规划 舞蹈家怀特
    分数规划
    Keywords Search AC自动机
    Minimum Transport Cost Floyd 输出最短路
    A Simple Problem with Integers 线段树 区间更新 区间查询
    CF519E A and B and Lecture Rooms
  • 原文地址:https://www.cnblogs.com/Ame-sora/p/13669233.html
Copyright © 2011-2022 走看看