点这里进入我的博客,获得更好的体验!
搜索框中搜索这篇博客即可,已做迁移,支持一下咯~
题意:
给你x y z k的范围,找到满足式子x+y+z = k 的个数
思路:
我们考虑这样一个问题:
有n个非零整数
x
1
,
x
2
,
x
3
.
.
.
x
n
x_1,x_2,x_3...x_n
x1,x2,x3...xn 分别给出n个数的取值范围,求满足
x
1
+
x
2
+
x
3
+
.
.
.
+
x
n
=
M
x_1+x_2+x_3+...+x_n=M
x1+x2+x3+...+xn=M的方案数。
解法:
先考虑理想情况:n个数取值范围无限大,此时
x
i
≥
0
x_i \geq0
xi≥0,于是我们令
y
1
=
x
1
+
1
,
y
2
=
x
2
+
1...
y
n
=
x
n
+
1
y_1 = x_1+1,y_2 = x_2+1...y_n=x_n+1
y1=x1+1,y2=x2+1...yn=xn+1,那么问题就转换成求
y
1
+
y
2
+
.
.
.
+
y
n
=
M
+
N
y_1+y_2+...+y_n=M+N
y1+y2+...+yn=M+N的方案数,此时
y
i
≥
0
y_i \geq0
yi≥0,故我们用隔板法:在M+N-1个空隙中插入N-1个板,故方案数为:
C
M
+
N
−
1
N
−
1
C_{M+N-1}^{N-1}
CM+N−1N−1,但由于实际情况中,
x
1
≤
A
1
,
x
2
≤
A
2
.
.
.
x
n
≤
A
n
x_1\leq A_1,x_2\leq A_2...x_n\leq A_n
x1≤A1,x2≤A2...xn≤An,由于直接求满足限制条件的方案很难,那我们就尝试求其补集,再用全集(就是没有限制条件时的方案数)减去即可。设不满足
x
i
≤
A
i
x_i \leq A_i
xi≤Ai的方案的集合为
S
i
S_i
Si,则答案为
C
M
+
N
−
1
N
−
1
−
∣
S
1
∪
S
2
.
.
.
∪
S
n
∣
C_{M+N-1}^{N-1}-|S_1 \cup S_2 ... \cup S_n|
CM+N−1N−1−∣S1∪S2...∪Sn∣,运用容斥原理,答案为
C
M
+
N
−
1
N
−
1
−
∣
S
1
∣
−
∣
S
2
∣
−
.
.
.
−
∣
S
n
∣
+
∣
S
1
∩
S
2
∣
+
∣
S
2
∩
S
3
∣
+
.
.
.
−
∣
S
1
∩
S
2
∩
S
3
∣
−
.
.
.
C_{M+N-1}^{N-1}-|S_1|-|S_2|-...-|S_n|+|S1 \cap S_2|+|S_2 \cap S_3|+...-|S_1 \cap S_2 \cap S_3|-...
CM+N−1N−1−∣S1∣−∣S2∣−...−∣Sn∣+∣S1∩S2∣+∣S2∩S3∣+...−∣S1∩S2∩S3∣−...(具有查百度,就是一个容斥展开式)。对于
∣
S
i
∣
|S_i|
∣Si∣其实就是先取出
A
i
+
1
A_i+1
Ai+1给
y
i
y_i
yi(这样的操作使得不满足第i个限制条件,符合
S
i
S_i
Si的定义),然后就变成了在剩下的
M
+
N
−
1
−
(
A
i
+
1
)
M+N-1-(A_i+1)
M+N−1−(Ai+1)个空隙中插入N-1个隔板,即
C
M
+
N
−
2
−
(
A
i
+
1
)
N
−
1
C_{M+N-2-(A_i+1)}^{N-1}
CM+N−2−(Ai+1)N−1同理
∣
S
i
∩
S
j
∣
=
C
M
+
N
−
2
−
(
A
i
+
1
)
−
(
A
j
+
1
)
N
−
1
|S_i \cap S_j| =C_{M+N-2-(A_i+1)-(A_j+1)}^{N-1}
∣Si∩Sj∣=CM+N−2−(Ai+1)−(Aj+1)N−1
a
n
s
=
C
M
+
N
−
1
N
−
1
−
∑
i
=
1
N
C
M
+
N
−
2
−
(
A
i
+
1
)
N
−
1
+
∑
i
<
j
N
C
M
+
N
−
2
−
(
A
i
+
1
)
−
(
A
j
+
1
)
N
−
1
−
.
.
.
+
.
.
.
ans=C_{M+N-1}^{N-1}-\displaystyle \sum_{i = 1}^{N}C_{M+N-2-(A_i+1)}^{N-1}+\displaystyle \sum_{i <j }^{N}C_{M+N-2-(A_i+1)-(A_j+1)}^{N-1}-...+...
ans=CM+N−1N−1−i=1∑NCM+N−2−(Ai+1)N−1+i<j∑NCM+N−2−(Ai+1)−(Aj+1)N−1−...+...
现在回到这道题就很容易了,直接给出式子: a n s = C k + 3 − 1 2 − C k + 3 − 1 − ( a + 1 ) 2 − C k + 3 − 1 − ( b + 1 ) 2 − C k + 3 − 1 − ( c + 1 ) 2 + C k + 3 − 2 − ( a + 1 ) − ( b + 1 ) 2 + C k + 3 − 1 − ( a + 1 ) − ( c + 1 ) 2 + C k + 3 − 1 − ( b + 1 ) − ( c + 1 ) 2 − C k + 3 − 1 − ( a + 1 ) − ( b + 1 ) − ( c + 1 ) 2 ans=C_{k+3-1}^{2}-C_{k+3-1-(a+1)}^{2}-C_{k+3-1-(b+1)}^{2}-C_{k+3-1-(c+1)}^{2}+C_{k+3-2-(a+1)-(b+1)}^{2}+C_{k+3-1-(a+1)-(c+1)}^{2}+C_{k+3-1-(b+1)-(c+1)}^{2}-C_{k+3-1-(a+1)-(b+1)-(c+1)}^{2} ans=Ck+3−12−Ck+3−1−(a+1)2−Ck+3−1−(b+1)2−Ck+3−1−(c+1)2+Ck+3−2−(a+1)−(b+1)2+Ck+3−1−(a+1)−(c+1)2+Ck+3−1−(b+1)−(c+1)2−Ck+3−1−(a+1)−(b+1)−(c+1)2
由于这道题的k有范围要求,直接遍历一遍所以情况把每种情况方案数加起来就行,给出代码:
LL C(int n, int m) {
if(n <= 0) return 0;
LL res = 1;
for(int i = n; i >= n-m +1; i--) {
res *= i;
}
for(int i = 2; i<=m; i++){
res /= i;
}
return res;
}
int main(int argc, char const *argv[]) {
ios::sync_with_stdio(false);
int a ,b, c, d;
LL ans = 0;
cin >> a >> b >> c >> d;
for(int i = 0; i <= d; i++){
ans+= C(i+2,2) - C(i+2-a-1,2) - C(i+2-b-1,2) - C(i+2-c-1,2) + C(i+2-a-b-2,2) + C(i+2-a-c-2,2) + C(i+2-b-c-2,2) - C(i+2-a-b-c-3,2);
}
cout << ans << endl;
return 0;
}
Hard版本题目:
Acwing 214.Devu和鲜花
这道题就是更一般的情况了,要用到上面推导的结论。这道题N的数据范围很小,我们把每一个条件看成一个二进制位,如果是1或0就代表遵守或不遵守这个条件,奇数个1就减,偶数个1就加,举例:
S
2
∩
S
4
∩
S
7
S_2 \cap S_4 \cap S_7
S2∩S4∩S7可以表示为0101001。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
LL m,A[30];
int qmi(int a, int b, int p){//快速幂求逆元
int res = 1;
while(b) {
if(b&1) res = (LL) res * a % p;
b >>= 1;
a = (LL)a * a % p;
}
return res;
}
int down = 1;
int C(LL a, LL b){
if(a < b) return 0;
int up = 1;
for(LL i = a; i >= a - b + 1; i--) up = i % mod * up % mod;
return (LL)up * down % mod;//由于数据大会超LL要先取模,利用逆元化除为乘(除法不能先取模)
}
int main(){
int n;
cin >> n >> m;
for(int i = 1; i <= n-1; i++) down = (LL)down * i % mod;
down = qmi(down, mod-2, mod);//费马小定理求逆元
for(int i = 0; i < n; i++){
cin >> A[i];
}
int ans = 0;
for(int i = 0; i < 1 << n; i++){
LL a = m+n-1, b = n-1;
int flag = 1;
for(int j = 0; j < n; j++){
if(i >> j & 1){
flag *=-1;
a -= A[j]+1;
}
}
ans = (ans + flag * C(a,b))%mod;
}
cout << (ans + mod) %mod << endl;
}