蓝书(算法竞赛进阶指南)刷题记录——Codeforces 451 E Devu and Flowers(容斥原理+Lucas定理)

本文详细解析了CF451E题目的算法思路,针对给定多种花束的组合问题,阐述了如何在限制条件下计算出选择特定数量花束的所有可能方案数。讨论了当所需花束数量小于等于或大于每种花束可用数量时的不同计算方法,以及如何通过枚举和组合数学原理来避免重复计数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:CF451E.
题目大意:给定 n n n种花,第 i i i种花有 A i A_i Ai束,现在要选出 m m m束花,问方案数.
1 ≤ n ≤ 20 , 0 ≤ m ≤ 1 0 14 , 0 ≤ A i ≤ 1 0 12 1\leq n\leq 20,0\leq m\leq 10^{14},0\leq A_i\leq 10^{12} 1n20,0m1014,0Ai1012.

我们可以知道如果 m ≤ A i m\leq A_i mAi,那么方案数就是 C n − 1 n + m − 1 C_{n-1}^{n+m-1} Cn1n+m1.

如果 m > A i m>A_i m>Ai,那么我们就需要把所有方案中有任意一种花 i i i选超过了 A i A_i Ai束的全部去掉.

考虑对于任意一个 i i i,先选入 A i + 1 A_i+1 Ai+1束第 i i i种花,剩下还有 C n − 1 n + m − A i − 2 C_{n-1}^{n+m-A_i-2} Cn1n+mAi2中方案需要减去.然后发现这里面又有重复的,即有两种花 i , j i,j i,j都超过的,那么这样子的有方案数 C n − 1 n + m − A i − A j − 3 C_{n-1}^{n+m-A_i-A_j-3} Cn1n+mAiAj3需要加上.以此类推.

具体如何实现只要 O ( 2 n ) O(2^n) O(2n)枚举每一个数,然后计算方案数,设选中的数为 k k k,则这一项的系数为 ( − 1 ) k (-1)^k (1)k,并且同时计算组合数.不过由于组合数过大,需要用到Lucas把 n + m − 1 n+m-1 n+m1对模数取模,而且组合数中下面部分太大,上面部分却很小,所以要用 O ( m ) O(m) O(m)复杂度递推 C n m C_{n}^{m} Cnm.

时间复杂度 O ( n 2 n ) O(n2^n) O(n2n).

代码如下:

#include<bits/stdc++.h>
  using namespace std;
  
#define Abigail inline void
typedef long long LL;

const int N=20;
const LL mod=1000000007;

int n;
LL m,a[N+9],inv[N+9],ans=0;

void pre_inv(int n){
  inv[1]=1;
  for (int i=2;i<=n;++i)
    inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}

LL C(LL n,LL m){
  if (n<0||m<0||n<m) return 0;
  n%=mod;
  LL ans=1;
  for (LL i=1;i<=m;++i)
    ans=ans*inv[i]%mod*((n-i+1)%mod)%mod;
  return ans;
}

Abigail into(){
  scanf("%d%I64d",&n,&m);
  for (int i=1;i<=n;++i)
    scanf("%I64d",&a[i]);
}

Abigail work(){
  LL t,k;
  pre_inv(n);
  for (int i=0;i<1<<n;++i){
    t=n+m-1;k=1;
    for (int j=1;j<=n;++j)
	  if (i>>j-1&1) t-=a[j]+1,k=-k;
	ans+=k*C(t,(LL)n-1);
	ans=(ans%mod+mod)%mod;
  }
}

Abigail outo(){
  printf("%I64d\n",ans);
}

int main(){
  into();
  work();
  outo();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值