【题解】NOIp模拟:数字
程序员文章站
2024-03-18 22:08:04
...
数字
(num.c/cpp/pas)
【问题描述】
一个数字被称为好数字当他满足下列条件:
1. 它有2*n个数位,n是正整数(允许有前导0)。
2. 构成它的每个数字都在给定的数字集合S中。
3. 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等
例如对于n=2,S={1,2},合法的好数字有1111,1122,1212,1221,2112,2121,2211,2222这样8种。
已知n,求合法的好数字的个数mod 999983。
【输入格式】
第一行一个数n。
接下来一个长度不超过10的字符串,表示给定的数字集合。
【输出格式】
一行一个数字表示合法的好数字的个数mod 999983。
【样例输入】
2
0987654321
【样例输出】
1240
【数据规模】
对于20%的数据,n≤7。
对于100%的.据,n≤1000,|S|≤10。
先考虑前个和后个和相等的时候
表示前位,总和为的方案数
因为前后两部分相等,方案数为
然后把后部分插到前部分里面,相邻插入,可以得到奇偶相等,所以前后相等与奇偶相等是等价的,所以答案是
以上是没有去重的,我们发现两种情况会出现重复的情况
需要去重的是既满足前后相等,又满足奇偶相等的情况
把一排长度为的数列,切成4块,每块都有个数,若是奇数的话,第一个块比第二个块多一个,第四个块比第三个块多一个
然后令这四个块的和分别为
前后相等得到
把后面一半插到前面一半,得到
联立两式得到
所以枚举的值,这样的方案数就是
减掉这部分就好了
Code:
#include <bits/stdc++.h>
#define maxn 1010
#define maxm 9010
#define LL long long
using namespace std;
const int qy = 999983;
int n, a[maxn], m;
char s[maxn];
LL dp[2][maxm], f[maxm], g[maxm];
int main(){
freopen("num.in", "r", stdin);
freopen("num.out", "w", stdout);
scanf("%d", &n);
scanf("%s", s + 1);
for (int i = 1; i <= strlen(s + 1); ++i) a[++m] = s[i] - 48;
dp[0][0] = 1;
for (int i = 1; i <= n; ++i){
int now = i & 1, pre = now ^ 1;
for (int j = 0; j <= 9 * i; ++j) dp[now][j] = 0;
for (int j = 0; j <= 9 * (i - 1); ++j)
for (int k = 1; k <= m; ++k) (dp[now][j + a[k]] += dp[pre][j]) %= qy;
if (i == (n + 1) / 2) for (int j = 0; j <= i * 9; ++j) f[j] = dp[now][j];
if ((n & 1) && i == n / 2) for (int j = 0; j <= i * 9; ++j) g[j] = dp[now][j];
}
// for (int i = 1; i <= 9 * (n >> 1); ++i) printf("%d\n", f[i]);
int now = n & 1;
LL ans = 0;
for (int i = 0; i <= 9 * n; ++i) (ans += 2LL * dp[now][i] % qy * dp[now][i] % qy) %= qy;
if (n & 1){
int u = (n + 1) >> 1, v = n >> 1;
for (int i = 0; i <= u * 9; ++i)
for (int j = 0; j <= v * 9; ++j)
ans = ((ans - f[i] * f[i] % qy * g[j] % qy * g[j] % qy) % qy + qy) % qy;
} else{
int s = n >> 1;
for (int i = 0; i <= 9 * s; ++i)
for (int j = 0; j <= 9 * s; ++j) ans = ((ans - f[i] * f[i] % qy * f[j] % qy * f[j] % qy) % qy + qy) % qy;
}
printf("%lld\n", ans);
return 0;
}