Cherrt的第一篇的题解哦~
题意
若n个不同的屋子里各有一个人,求经过 k k k次串门后各个屋子里人数的不同格局的数量。答案对 1 0 9 + 7 10^9+7 109+7取模。
题解
首先,看一下数据范围,发现 k ≤ 1 0 9 k≤10^9 k≤109——所以时间复杂度完全不能依赖k。
接着我们发现,当 k ≥ n k≥n k≥n时,每种格局都是可能形成的。换句话说, k ≥ n k≥n k≥n时相当于将n个相同的苹果放入 n n n个盘子里,求出盘子里苹果数量的格局。这时可以使用插板法,即 C ( n − 1 , 2 ∗ n − 1 ) C(n-1,2*n-1) C(n−1,2∗n−1)(不懂请看福利)。
如果 k < n k<n k<n,那么就不可能形成所有格局。这时考虑将所有格局的数量( C ( n − 1 , 2 ∗ n − 1 ) C(n-1,2*n-1) C(n−1,2∗n−1))减去所有无法得到的格局的数量。
易得,当空房的数量大于 k k k时不合法,即非空房的数量小于等于 n − k n-k n−k时不合法。然后,我们可以枚举非空房的数量 i i i,累加即可。再次得到,当非空房的数量为i时,格局的数量为 C ( n , i ) ∗ C ( n − 1 , i − 1 ) C(n,i)*C(n-1,i-1) C(n,i)∗C(n−1,i−1)(不懂请看福利)。
于是思路就很清晰啦! 但是要注意一些东西,否则会死得一批得惨 。
卡点(注意点)
①逆元;
②刚才说过要减去不满足要求的格局数量,所以要始终保持答案非负性;
③做阶乘逆元的时候,数组一定要开大;否则RE。
事实上难度并不是很大······也就普及+/提高吧。
福利
我知道阁下可能不懂那两个式子怎么推出来的。不着急,看个例子就明白了。
将n个相同苹果放到k个不同的盘子里,每个盘子可以为空,求格局数量?
假设有 2 × k 2×k 2×k个苹果。我们可以考虑插板,即在 2 ∗ k − 1 2*k-1 2∗k−1个间隙中插板,两个板之间苹果的数量减一就是该盘子内苹果的数量。
这是最基本的小学奥数啊
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxlen=200000;
int n,k,mod=1e9+7,ans=0ll;
int jc[5*maxlen+5];
int quick_power(int a,int b)
{
int res=1;
for (;b;b=b>>1,a=(a*a)%mod)
{
if (b&1) res=(res*a)%mod;
}
return res;
}
int ny(int n)
{
return quick_power(n,mod-2)%mod;
}
int C(int n,int k)
{
if (k<=0||n==k) return 1;
if (2*k>n) k=n-k;
return ((jc[n]*ny(jc[n-k])%mod)*ny(jc[k]))%mod;
}
inline void get_all_jc()
{
jc[0]=1;
for (int i=1;i<=5*maxlen;i++) jc[i]=(jc[i-1]*i)%mod;
}
signed main()
{
cin>>n>>k;
get_all_jc();
if (k>n) return cout<<C(2*n-1ll,n)<<endl,0;
else
{
ans=C(2*n-1ll,n);
for (int i=1;i<=n-k-1;i++) ans=((ans-C(n,i)*C(n-1,i-1))%mod+mod)%mod;
return cout<<ans<<endl,0;
}
}
撒花✿✿ヽ(°▽°)ノ✿撒花