2018 Multi-University Training Contest 8 1001 Character Encoding【容斥】

题意:每个数字的取值范围为0到n-1,共有m个数字,求总和为k的方案数。

因为k的上限为1e5,那么我们不妨把k看做k个1,然后通过m-1个隔板来分割出m个数字。但是这么做会有一个问题,那就是数字可能为0。那么不妨将数字的取值范围改成1-n,m个数所以k要相应的变成k+m,那么就可以使用隔板法了。

先无限制的求一下总数:C(m+k-1,m-1)

如果我们现在拿出来n个1,将剩下的数分割成m个数,那么把这n个1放到任何位置上,都是至少有一个数字超出限制的。

也就是:C(m+k-1,m-1)-C(m,1)*C(m+k-1-n,m-1)+C(m,2)*C(m+k-1-2n,m-1)....  C(m,x)*C(m+k-1-xn,m-1)   //奇 偶 判断加减

如此反复直到不合法结束,到x结束也就是 到无法满足至少x个数超过限制时结束。

比赛的时推了半天不知道为什么没有推导出来,很难受,只想到了O((k/n)^2)的算法,相当的难受啊

 

看了dls的讲解,顺便学习了一波推生成函数的方法

题意很简单,就是将k分成n个数,每个数的范围是[0,n-1],问一共有多少中方法

 

我们先不看k,只看n和m

当n=2时

     0  1   2   3   4   5   6   7   8  9 10 11...

0   1

1   1  1

2   1  2   1

3   1  3   3   1

4   1  4   6   4   1

5   1  5  10  10   5   1

6   1  6  15  20  15   6   1

7   1  7  21  35  35  21   7   1

8   1  8  28  56  70  56  28   8   1

9   1  9  36  84 126 126  84  36   9  1

10  1 10  45 120 210 252 210 120  45 10  1

11  1 11  55 165 330 462 462 330 165 55 11  1

很显然就是杨辉三角,直接二项式定理就可以了

当n=3是,也是类似的

     0   1   2   3   4    5    6      7      8     9   10  11  12

0:  1

1:  1   1   1

2:  1   2   3    2    1

3:  1   3   6    7    6   3    1

4:  1   4  10  16  19  16  10   4      1

5:  1   5  15  30  45  51  45   30   15    5   1

6:  1   6  21  50  90 126 141 126  90  50 21  6  1

 

生成公式:

(1+x+x^{2}+...+x^{n}) ^{m}

这个公式就是就可以表示第m行的结果,对应的系数就是展开式中x^i的系数,所以我们只需要求生成公式中x^k的系数即可。

那么我们就得对公式化简

(1+x+x^{2}+...+x^{n-1}) ^{m} \\ =(\frac{1-x^{n}}{1-x}) ^{m} \\ =(1-x^{n})^{m} (1-x) ^{-m}

根据泰勒展开:

可得

(1-x) ^{-m}=\sum_{i=0}^{\infty}C_{m+i-1} ^{i-1}\cdot x^{i}

所以

(1+x+x^{2}+...+x^{n-1}) ^{m} \\ =(\frac{1-x^{n}}{1-x}) ^{m} \\ =(1-x^{n})^{m} (1-x) ^{-m} \\ =\sum_{i=0}^{m} C_{m} ^{i} (-x^{n}) ^{i} \cdot \sum_{i=0}^{\infty} C_{m+i-1} ^{i-1}\cdot x^{i}

到了这一步,我们就可以直接计算了

 

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;
#define  LL long long

const LL N = 3e5 + 10;
const LL MAXN = 3e5;
const LL mod = 998244353;
LL fac[N];
LL inv[N];
LL qkm(LL base,LL b)
{
    LL ans=1;
    while(b!=0){
        if(b&1!=0) ans=ans*base%mod;
        base=base*base%mod;
        b>>=1;
    }
    return ans;
}

LL C(LL n,LL m)
{
    if(m>n) return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

int main()
{

    fac[0]=fac[1]=1;
    for(LL i=2;i<=MAXN;i++)
        fac[i]=fac[i-1]*i%mod;
    inv[MAXN]=qkm(fac[MAXN],mod-2);
    for(LL i=MAXN-1;i>=0;i--)
        inv[i]=inv[i+1]*(i+1)%mod;
      /* for(int i=MAXN;i>=MAXN-10;i--)
            cout<<inv[i]%mod<<" ";
        cout<<endl;*/

    LL T;
    cin>>T;
    while(T--){
        LL n,m,k;
        cin>>n>>m>>k;
        LL ans=C(k+m-1,m-1);
        LL p=1;
        LL di = k+m-1-n;
        LL num=1;
        while(di>=m-1){
            if(p){
                ans=ans-C(m,num)*C(di,m-1)%mod+mod;
                ans%=mod;
            }else{
                ans=ans+C(m,num)*C(di,m-1)%mod;
                ans%=mod;
            }
            p=p^1;
            num++;
            di-=n;
        }
        cout<<ans<<endl;
    }
    return 0;
}

转载:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1001&cid=809

转载: https://blog.csdn.net/njupt_lyy/article/details/81714203

关于生成函数:http://www.matrix67.com/blog/archives/120

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值