方法:组合数+不定方程解的个数
解析:
毒瘤题,好难啊,刷一个我就不想再刷另外两个了
![这里写图片描述](https://img-blog.csdn.net/20150826195159677)
这玩意的话,后俩先不看。
先看前两个。
前两个是啥意思呢。
不妨考虑一下挡板法。
这里我举一个某篇我想不起名字的论文的方法。
首先看这个不定方程
x+y+z=20
我们设一个字符串aaaaaaaaaaaaaaaaaaaa
恰好20个a,现在来给x,y分配。
则该求解该不定方程的正整数解的个数相当于将串变成了:
asasasasasasasasasasasasasasasasasasasa
20个a,19个s
从中任意选出两个S
则答案显然为
C219
这也就恰好对应了上文的
Cn−1m−1
然后现在我们来考虑该不定方程的非负整数解的个数。
换一种假设方式。
现在假设有20个白球,2个黑球。
则这22个球的排列方案数对应着该不定方程的解的个数。
很显然嘛。
你可以画一下俩黑球在队首队尾的特殊情况理解一下就好了。
也可以想这两个黑球将一个线段分成了三部分。
总之这个不是什么难的地方。
于是答案对应是什么呢?
C222
即公式里对应的
Cn−1n+m−1
第三个公式我还没咋看,没研究,再说这道题跟这个也没关系,所以先挖坑
第四个公式的话其实就是个容斥原理!
枚举,n个数里面有多少个大于等于k+1个数。
那么这显然是个容斥。
设F(i)表示n个数里有i个大于等于k+1
则
ans=F(0)−F(1)+F(2)−F(3).....
好,现在来说这道题。
我们枚举第一个猴子能取多少个桃。
倒序枚举+一个剪枝即可
然后现在对于剩下的方程是这个形式。
X2+X3+...+Xn=m−X1且Xi(i∈[2,n])<=X1
裸上公式四。
然后我们来观察一下复杂度。
其实这个东西的复杂度是
∑ni=1ni
这是啥?->级数求和啊。
O(nlogn)啊
然后预处理是O(nlogmod)的
显然能过。
代码:
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));
}
}