HDU 5201 The Monkey King 组合数+不定方程解的个数

题意:链接

方法:组合数+不定方程解的个数

解析:

毒瘤题,好难啊,刷一个我就不想再刷另外两个了

这里写图片描述

这玩意的话,后俩先不看。

先看前两个。

前两个是啥意思呢。

不妨考虑一下挡板法。

这里我举一个某篇我想不起名字的论文的方法。

首先看这个不定方程

x+y+z=20

我们设一个字符串aaaaaaaaaaaaaaaaaaaa

恰好20个a,现在来给x,y分配。

则该求解该不定方程的正整数解的个数相当于将串变成了:

asasasasasasasasasasasasasasasasasasasa

20个a,19个s

从中任意选出两个S

则答案显然为 C219

这也就恰好对应了上文的 Cn1m1

然后现在我们来考虑该不定方程的非负整数解的个数。

换一种假设方式。

现在假设有20个白球,2个黑球。

则这22个球的排列方案数对应着该不定方程的解的个数。

很显然嘛。

你可以画一下俩黑球在队首队尾的特殊情况理解一下就好了。

也可以想这两个黑球将一个线段分成了三部分。

总之这个不是什么难的地方。

于是答案对应是什么呢?

C222

即公式里对应的 Cn1n+m1


第三个公式我还没咋看,没研究,再说这道题跟这个也没关系,所以先挖坑


第四个公式的话其实就是个容斥原理!

枚举,n个数里面有多少个大于等于k+1个数。

那么这显然是个容斥。

设F(i)表示n个数里有i个大于等于k+1

ans=F(0)F(1)+F(2)F(3).....


好,现在来说这道题。

我们枚举第一个猴子能取多少个桃。

倒序枚举+一个剪枝即可

然后现在对于剩下的方程是这个形式。

X2+X3+...+Xn=mX1Xi(i[2,n])<=X1

裸上公式四。

然后我们来观察一下复杂度。

其实这个东西的复杂度是

ni=1ni

这是啥?->级数求和啊。

O(nlogn)啊

然后预处理是O(nlogmod)的

显然能过。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100100
#define mod 1000000007
using namespace std;
typedef long long ll;
int t;
int n,m;
ll factor[N<<1];
ll invfactor[N<<1];
ll get_inv(ll x,ll y)
{
    ll ret=1;
    while(y)
    {
        if(y&1)ret=(ret*x)%mod;
        x=(x*x)%mod;
        y>>=1; 
    }
    return ret;
}
void init_factor()
{
    factor[0]=1,factor[1]=1,invfactor[1]=1,invfactor[0]=1;
    for(int i=2;i<=200000;i++)
    {
        factor[i]=(factor[i-1]*i)%mod;
        invfactor[i]=get_inv(factor[i],mod-2);
    }   
}
ll get_C(int n,int m)
{
    if(m==0)return 1;
    return factor[n]*invfactor[m]%mod*invfactor[n-m]%mod; 
} 
ll get_ans(int n,int m)
{
    //x1+x2+x3+..+xm==n
    //x1最大
    ll ret=0;
    if(n==0)return 0;
    if(n==1)return 1;
    if(m==1)return 1;
    for(int i=n;i>0;i--)
    {
        if(i<=(n-i)/(m-1)+((n-i)%(m-1)==0?0:1))break;
        int tmpn=n-i,tmpm=m-1;
        ll ans=0;
        for(int j=0;j<=tmpm&&j*i<=tmpn;j++)
        {
            ll caltmp=get_C(tmpm,j)*get_C(tmpn-j*i+tmpm-1,tmpm-1)%mod;
            if(j%2==0)ans=(ans+caltmp)%mod;
            else ans=((ans-caltmp)%mod+mod)%mod; 
        }
        ret=(ret+ans)%mod; 
    }
    return ret;
}
int main()
{
    init_factor();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        printf("%I64d\n",get_ans(n,m));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值