【题解】NOIp模拟:数字

数字
(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。

先考虑前 n n 个和后 n n 个和相等的时候
d p i , j dp_{i,j} 表示前 i i 位,总和为 j j 的方案数
d p i , j + a k + = d p i 1 , j dp_{i,j+a_k}+=dp_{i-1,j}
因为前后两部分相等,方案数为 i = 0 9 n d p n , i 2 \sum_{i=0}^{9n}dp_{n,i}^2
然后把后部分插到前部分里面,相邻插入,可以得到奇偶相等,所以前后相等与奇偶相等是等价的,所以答案是 i = 0 9 n 2 d p n , i 2 \sum_{i=0}^{9n}2dp_{n,i}^2

以上是没有去重的,我们发现两种情况会出现重复的情况
需要去重的是既满足前后相等,又满足奇偶相等的情况
把一排长度为 2 n 2n 的数列,切成4块,每块都有 n 2 \frac{n}{2} 个数,若 n n 是奇数的话,第一个块比第二个块多一个,第四个块比第三个块多一个
然后令这四个块的和分别为 a , b , c , d a,b,c,d
前后相等得到 a + b = c + d a+b=c+d
把后面一半插到前面一半,得到 a + c = b + d a+c=b+d
联立两式得到 a = d , b = c a=d,b=c
所以枚举 a , b a,b 的值,这样的方案数就是 d p [ n 2 ] , a 2 d p n [ n 2 ] , b 2 dp_{[\frac{n}{2}],a}^2*dp_{n-[\frac{n}{2}],b}^2
减掉这部分就好了

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;
}

猜你喜欢

转载自blog.csdn.net/ModestCoder_/article/details/108238607