两道容斥计数题

两道容斥计数

CF451E. Devu and Flowers

题意

\(n(1\le n\le 20)\) 个不同颜色的球,每种颜色的球有 \(f_i(1\le f_i \le 10^{12})\) 个,问拿 \(s(1\le s\le 10^{14})\) 个球的方案数。

题解

考虑生成函数

\[F(x)=\prod_{i=1}^n(\sum_{j=0}^{f_i}x_j)=\frac{\prod_{i=1}^n(1-x^{f_i+1})}{(1-x)^n}=(\prod_{i=1}^n(1-x^{f_i+1}))\times(\sum_{i=0}\binom{n+i-1}{i}x^i) \]

求这玩意 \(x^s\) 的系数,因为分子的幂最多就 \(2^n\) 中,把分子每个幂的贡献算一下就完事。

AC代码

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
using namespace std;
using ll = long long;

constexpr int N = 55, P = 1e9+7;
ll fac[N];

ll qpow(ll a, ll b) {
	ll res=1;
	for(;b;b>>=1,a=a*a%P)if(b&1)res=res*a%P;
	return res;
}

ll C(ll n, ll k) {
	if (k > n) return 0;
	if (k > n-k) k = n-k;
	ll res = 1;
	rep(i,0,k)res=(n-i)%P*res%P;
	return res*qpow(fac[k], P-2)%P;
}

void pre() {
	fac[0] = 1;
	rep(i,1,50) fac[i] = fac[i-1]*i%P;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
#endif
	cin.tie(nullptr)->ios::sync_with_stdio(false);
	pre();
	ll n,s,ans=0; cin>>n>>s;
	vector<ll> a(n);
	rep(i,0,n)cin>>a[i];
	rep(st,0,(1<<n)){
		ll pw=0, coe=0;
		rep(i,0,n){
			if((st>>i)&1){
				pw+=a[i]+1;
				coe ++;
			}
		}
		if(pw>s) continue;
		if(coe&1) ans=(ans-C(n+s-pw-1,n-1)+P)%P;
		else ans=(ans+C(n+s-pw-1,n-1))%P;
	}
	cout<<ans<<"\n";
	return 0;
}

注意第 20 行,必须先模后乘,否则爆long long。
本题也有容斥做法,也就是枚举超出其范围的那些。也就是0个违法-1个违发+2个违法。。。

下面以CCPC2021 威海M举例。

CCPC2021 威海M 810975

题意

给定 \(n,m,k\) ,问有多少个长度为 \(n\)\(01\) 串,其中有 \(m\)\(1\) ,且 连续的 \(1\) 的个数不能超过 \(m\) ,且要确切的有一个长度为 \(k\) 的连续的 \(1\)

题解

考虑容斥。其中,“确切”的有一个长度为 \(k\) 的连续的 \(1\) ,可以容斥掉,也即:最大连续的 \(1\) 的长度为 \([0,k]\) 的减去 \([0,k-1]\) 的就行。

把这个条件去掉之后,由于有 \(n-m\)\(0\) ,考虑隔板法,有 \(n-m+1\) 个空,于是得到如下方程:

\[\sum_{i=1}^{n-m+1}x_i=m,其中x_i\in [0,k] \]

枚举大于 \(k\)\(x_i\) 的个数,将这些 \(x_i\) 减去 \(k+1\) ,那等式右边就成了 \(m-num\times (k+1)\) ,就化成没有约束一般问题了。

时间复杂度 \(O(n)\)

AC代码

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
using namespace std;
using ll = long long;

constexpr int N = 1e5+5, P = 998244353;
ll fac[N], inv[N];
ll n,m,k;

ll qpow(ll a, ll b) {
	ll res=1;
	for(;b;b>>=1,a=a*a%P)if(b&1)res=res*a%P;
	return res;
}

ll C(ll a, ll b) {
	if (b > a) return 0;
	return fac[a]*inv[b]%P*inv[a-b]%P;
}

ll calc(ll d) {
	ll ans=0;
	d++;
	rep(i,0,n-m+2){
		if(i&1)ans=(ans-C(n-m+1,i)*C(n-i*d,n-m)%P+P)%P;
		else ans=(ans+C(n-m+1,i)*C(n-i*d,n-m)%P)%P;
	}
	return ans;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
#endif // !ONLINE_JUDGE
   	cin.tie(nullptr)->ios::sync_with_stdio(false);
    cin>>n>>m>>k;
	if(m>n){
		cout<<0;
		return 0;
	}
	if(k>m){
		cout<<0;
		return 0;
	}
	fac[0] = inv[0] = 1;
	rep(i,1,N)fac[i]=fac[i-1]*i%P;
	inv[N-1] = qpow(fac[N-1],P-2);
	for(int i=N-2;i>=1;i--)inv[i]=inv[i+1]*(i+1)%P;
	cout<<(calc(k)-calc(k-1)+P)%P<<"\n";
    return 0;
}
posted @ 2021-11-30 01:48  xDaniel  阅读(97)  评论(0编辑  收藏  举报