• HDU 5201 The Monkey King


    题目链接

    题意

    (n)个桃子,分给(m)个猴子,每只猴子分到的桃子数为非负整数。

    要求第一只猴子分到的桃子严格多余剩下(m-1)只猴子中任意一只。求分桃子的方案数。

    分析

    首先枚举第一只猴子分到的桃子(x)

    不考虑限制条件的话,剩余(m - 1)只猴子分(n - x)个桃子,其方案数(P(n - x, m - 1) = C_{n - x + m - 2}^{m - 2})

    我们在这里用到了隔板法。

    隔板法

    (P(n,m))表示把(n)个桃子分给(m)个猴子,其中每只猴子都得到非负整数个桃子。

    我们新加入(m)个桃子,给每个猴子一个,总共有了(n + m)个桃子,每只猴子分到的桃子数变成正整数个。

    (n + m)个桃子分成(m)份,相当于把(n + m)个桃子排成一排,然后选择(m - 1)个断点,将桃子分成(m)段。

    任意两个桃子之间有一个可用的断点,总共有(n + m - 1)个断点可以选择。

    (P(n,m) = C_{n + m - 1}^{m - 1})

    隔板法结束

    现在考虑限制条件,即(m - 1)只猴子分到的桃子(c_i < x)

    考虑有几只猴子分到的桃子数(c_i geq x)

    假设至少(s)只猴子不满足(c_i < x)

    (c_1 geq x , c_2 geq x, dots, c_s geq x, c_{s+1} geq 0, dots, c_{m-1} geq 0)这里强行把(s)只猴子排在最前面,实际上是随机的(s)只猴子

    (P(n,m))处理的是(c_1 geq 0, c_2 geq 0, dots, c_{m} geq 0)的分法问题,我们需要调整一下上面的式子,让其变为与(P)函数相关的式子。

    (g(n - x, m - 1, s))表示(c_1 geq x , c_2 geq x, dots, c_s geq x, c_{s+1} geq 0, dots, c_{m-1} geq 0)的分法。

    (g(n - x, m - 1, s) = g(n - x - s * x, m - 1, 0) = P(n - x - s * x , m - 1))

    即我们对(s)只猴子中的每一只猴子都拿走(x)个桃子,使其变为(c_i geq 0)的形式。同时总桃子数减少(s * x)

    所以当有(s)只猴子不满足条件时,方案数为(C_{m-1}^{s} * P(n - x - s * x , m - 1))

    容斥

    答案(Ans = sum_{s = 0}^{m - 1} (-1)^{s} C_{m-1}^{s} * P(n - x - s * x , m - 1))

    答案等于至少(0)猴子-至少(1)猴子+至少(2)猴子......

    注意各种不合实际的情况要break

    容斥结束

    特判(n=1)(m=1)!

    
    #include<bits/stdc++.h>
    using namespace std;
    
    const int mod = 1e9 + 7;
    const int N = 1e6 + 50;
    
    int n,m;
    int fac[N + 10], inv[N + 10];
    int ksm(int x,int y){
    	int z = 1;
    	while(y){
    		if(y & 1) z = 1ll * z * x % mod;
    		y >>= 1;
    		x = 1ll * x * x % mod;
    	}
    	return z;
    }
    int C(int n,int m){
        if(n < m || m < 0 || n < 0) return 0;
        return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
    }
    int P(int n,int m){
        return C(n + m - 1, m - 1); 
    }
    
    signed main(){
        fac[0] = 1; for(int i = 1; i <= N; ++ i) fac[i] = 1ll * fac[i - 1] * i % mod;
        inv[N] = ksm(fac[N], mod - 2); for(int i = N - 1; i >= 0; -- i) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
        
        int T; scanf("%d",&T);
        while(T --){
            scanf("%d%d",&n,&m);
            if(n == 1 || m == 1) { puts("1"); continue; }
            int ans = 0;
            for(int i = 1; i <= n; ++ i){
                if(1ll * m * i - m  + 1 < n) continue;
    
                int ret = P(n - i, m - 1); int flag = -1;
                for(int j = 1; j < m && 1ll * (j + 1) * i <= n; ++ j){
                    if(n - i - 1ll * j * i < 0) break;
                    int x = 1ll * C(m - 1, j) * P(n - i - j * i, m - 1) % mod;
                    x = x * flag;
                    ret = ((ret + x) % mod + mod) % mod;
                    flag *= -1;
                }
                ans = (ans + ret) % mod;
            }
            printf("%d
    ",ans);
        }   
        return 0;
    }
    
    
  • 相关阅读:
    自定义的事件管理器
    解决修改表结构,添加外键时出现“约束冲突”的错误
    jQuery学习(二) 自定义扩展函数
    iBt(001-004)原文与试译
    老婆怀孕了!(5+6)
    Mac_如何打开系统文件library
    Mac_如何通过命令行装包到ios手机
    MAC干净卸载pycharm
    selenium自动化_如何启动safari浏览器
    selenium自动化_click方法点击无效
  • 原文地址:https://www.cnblogs.com/zzhzzh123/p/13445734.html
Copyright © 2020-2023  润新知