025 Roaming (Atcoder 5312)

  • 题目链接 Atcoder 5312
  • 题意:
    数组a[n],起初每个位置都为1,可以进行k次操作,把某个位置的1移到另一个位置,k次操作后得到一个最终数组,求最终数组的可能情况数。(可以与原数组重复)对1e9+7取模。
    3 ≤ n ≤ 2 e 5 3 \leq n \leq 2e5 3n2e5
    2 ≤ k ≤ 1 e 9 2\leq k\leq 1e9 2k1e9
  • 分析:
    原问题等价于n个球装进n个不同的箱子里,至多有k个箱子为空,问方案数。
    下面给出简单的证明:
    对于任意的 2 ≤ k 2 \leq k 2k,均能移动某个数字两次达成k-1个数为0,互换两个数的位置达成k-2个数为0,因此对于小于k的任何个数为0都有可能。与投球问题等价。
    新问题中,若 n < k nn<k所有方案数都能取到,反之则要减去 k + 1 → n − 1 k+1 \rightarrow n-1 k+1n1个空箱对应的方案数。
    总方案数为 ( n n + n − 1 ) { n\choose n+n-1} (n+n1n) n − i n-i ni个空箱对应的方案数 ( i n ) × ( n − i − 1 n − 1 ) {i\choose n} \times {n-i-1\choose n-1} (ni)×(n1ni1),也等于 i i i个空箱对应的方案数。(从n个箱子中选 i i i个不作为空箱,n个球装进n-i个箱子里,整理一下也可以写成这样 ( i n ) × ( i − 1 n − 1 ) {i\choose n} \times {i-1\choose n-1} (ni)×(n1i1)
  • 代码:
#include 
#define ll long long
using namespace std;
const int maxn=4e5+10;//要求2*n的组合数注意开两倍
const int mod=1e9+7;
ll fac[maxn],inv[maxn];
void gcd(ll a,ll b,ll &d,ll &x, ll &y){
	if(b){
		gcd(b,a%b,d,y,x); y-=x*(a/b);
	}
	else {
		d=a,x=1,y=0;
	}
}
void init(){
	fac[0]=fac[1]=1;
	for(int i=2;i<maxn;i++)
		fac[i]=fac[i-1]*i%mod;
}
ll c(ll n,ll k){
	ll x,y,d;
	ll tmp=(fac[k]*fac[n-k])%mod;
	gcd(tmp,mod,d,x,y);
	y=fac[n]*(x%mod)%mod;
	if(y<0) y+=mod;
	return y;
}
int main(){
	ll n,k;
	init();
	cin>>n>>k;
	ll ans=c(2*n-1,n);
	if(k>=n-1){
		//cout<<0;
		cout<<ans; return 0; 
	}
	else {
		for(int i=1;i<n-k;i++){
			ans-=c(n,i)*c(n-1,i-1)%mod;
			ans+=mod;
			ans%=mod;
		}
		cout<<ans;
	}
}

你可能感兴趣的:(AtCoder,组合数,数学)