Jzoj 3056NOIP2012模拟10.27容斥DP数学数字
Posted SSL_ZZL
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jzoj 3056NOIP2012模拟10.27容斥DP数学数字相关的知识,希望对你有一定的参考价值。
Link
题面
Description
一个数字被称为好数字当他满足下列条件:
- 它有2*n个数位,n是正整数(允许有前导0)
- 构成它的每个数字都在给定的数字集合S中。
- 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等
例如对于n=2,S={1,2},合法的好数字有1111,1122,1212,1221,2112,2121,2211,2222这样8种。
已知n,求合法的好数字的个数mod 999983。
Input
第一行一个数n。
接下来一个长度不超过10的字符串,表示给定的数字集合。
Output
一行一个数字表示合法的好数字的个数mod 999983。
Sample Input
2
0987654321
Sample Output
1240
Data Constraint
对于20%的数据,n≤7。
对于100%的.据,n≤1000,|S|≤10。
解题思路
设 f[i][j] 为 i 个位,和为 j 的方案数
这个随便求一下就好了,
f
[
i
]
[
j
]
+
=
f
[
i
−
1
]
[
j
−
a
[
k
]
]
f[i][j] += f[i-1][j-a[k]]
f[i][j]+=f[i−1][j−a[k]]
问题转换一下(容斥),前半段和=后半段和的方案 + 奇数位和=偶数位和的方案数 - 两种都满足的方案
前半段和=后半段和的方案:f[n][i] ^ 2
奇数位和=偶数位和的方案数:
其实可以发现奇数位全部移到左边,偶数位全部移到右边,然后又变成了前半段和=后半段和的方案
两种都满足的方案:这个就有点猥琐了
a为前半段奇数位和,b为前半段偶数位和,c为后半段奇数位和,d为后半段偶数位和
两种都满足
- a + b = c + d
- a + c = b + d
得出b = c,a = d
然后列几个数字发现,b = c和a = d的方案数是不一样的
因为如果 n 是个奇数,那么前半段的奇数位的个数 和 后半段的偶数位个数会更多
b = c方案数 * a = d方案数就是两种都满足的方案数
Code
#include <bits/stdc++.h>
#define P 999983
#define ll long long
using namespace std;
char a[10];
int n;
ll f[1010][9010], ans, k1, k2, maxn;
int main() {
scanf("%d", &n);
scanf("%s", a + 1);
int la = strlen(a + 1);
for(int i = 1; i <= la; i ++)
maxn = max(maxn, (ll)(a[i] - '0'));
f[0][0] = 1;
for(int i = 1; i <= n; i ++)
for(int j = 0; j <= n * maxn; j ++)
for(int k = 1; k <= la; k ++)
if(j >= (int)(a[k] - '0'))
f[i][j] = (f[i][j] + f[i - 1][j - (int)(a[k] - '0')]) % P;
for(int i = 0; i <= n * maxn; i ++)
ans = (ans + 2 * f[n][i] * f[n][i] % P) % P;
for(int i = 0; i <= (n + 1) / 2 * maxn; i ++)
k1 = (k1 + f[(n + 1) / 2][i] * f[(n + 1) / 2][i] % P) % P; //a = d的方案数
for(int i = 0; i <= n / 2 * maxn; i ++)
k2 = (k2 + f[n / 2][i] * f[n / 2][i] % P) % P; //b = c的方案数
ans = (ans - k1 * k2 % P + P) % P; //因为可能是负数所以先+P
printf("%lld", ans);
}
以上是关于Jzoj 3056NOIP2012模拟10.27容斥DP数学数字的主要内容,如果未能解决你的问题,请参考以下文章
Jzoj 3054NOIP2012模拟10.27倍增祖孙询问
10.27 noip模拟试题(afternoon)(跪在游戏玩少了2333)