Russian Code Cup 2017 - Finals C. Eleventh Birthday DP

版权声明:xgc原创文章,未经允许不得转载。 https://blog.csdn.net/xgc_woker/article/details/82929506

Description
给定n个数,求将这n个数按一定顺序拼在一起是11的倍数的方案数。


Sample Input
4
2
1 1
3
1 31 12
3
12345 67 84
9
1 2 3 4 5 6 7 8 9


Sample Output
2
2
2
31680


首先有一个性质,奇数位的和与偶数位的和相间的绝对值为11的倍数,那么这个数就为11的倍数。
那么其实对于一个偶数长度的数他放在序列的任何位置都不会对其它元素的奇偶性造成影响,可以考虑将偶数位的情况与奇数位的情况分开来处理。设f[i][j][k]为到第i个长度为奇数的数,有j个从偶数位开始,且当前数字余数为k。
可以得转移:
f [ i ] [ j ] [ k ] = f [ i 1 ] [ j ] [ k a [ i ] ] + f [ i 1 ] [ j 1 ] [ k + a [ i ] ] f[i][j][k]=f[i-1][j][k-a[i]]+f[i-1][j-1][k+a[i]]
长度为偶数转移类似。
那么就考虑将答案组合起来即可。
对于长度为奇数他最后的偶数位开始的数的个数一定是n1/2的。
那么这些数你可以随便乱排,就是(n1/2)!,剩下还有(n-(n1/2))个长度为奇数的数,也可以随便乱拍。
然后对于长度为偶数有多少个数从偶数位开始都没有问题,这些长度为偶数的就相当于插长度为奇数的数的空即可,于是可以组合数一下,对于它们来说这些数同样是可以随便乱排,再搞一下即可。。。


#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const LL mod = 998244353;
int _min(int x, int y) {return x < y ? x : y;}
int _max(int x, int y) {return x > y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

int a[2100], b[2100], o[2100];
LL f0[2][2010][11], f1[2][2010][11];
LL jc[2100], inv[2100];

LL pow_mod(LL a, LL k) {
	LL ans = 1;
	while(k) {
		if(k & 1) (ans *= a) %= mod;
		(a *= a) %= mod; k /= 2;
	} return ans;
}

LL C(int n, int m) {return jc[n] * inv[n - m] % mod * inv[m] % mod;}
LL calc(int n, int m) {
	if(n == 0) {
		if(m) return 0;
		else return 1;
	} return jc[m] * C(n + m - 1, m) % mod;
}

int main() {
	int tt = read();
	jc[0] = inv[0] = 1;
	for(int i = 1; i <= 2000; i++) jc[i] = jc[i - 1] * i % mod, inv[i] = pow_mod(jc[i], mod - 2);
	while(tt--) {
		int n = read(), n1 = 0, n2 = 0;
		for(int i = 1; i <= n; i++) {
			int x = read(), now = x, len = 0;
			while(now) now /= 10, ++len;
			if(len & 1) a[++n1] = x % 11;
			else b[++n2] = x % 11;
		}
		memset(f0[0], 0, sizeof(f0[0]));
		memset(f1[0], 0, sizeof(f1[0]));
		f0[0][0][0] = f1[0][0][0] = 1; int now0 = 0;
		for(int i = 1; i <= n1; i++) {
			now0 ^= 1; memset(f0[now0], 0, sizeof(f0[now0]));
			for(int j = 0; j <= i; j++) {
				for(int k = 0; k < 11; k++) {
					f0[now0][j][k] += f0[now0 ^ 1][j][(k - a[i] + 11) % 11];
					if(j) (f0[now0][j][k] += f0[now0 ^ 1][j - 1][(k + a[i]) % 11]) %= mod;
				}
			}
		} int now1 = 0;
		for(int i = 1; i <= n2; i++) {
			now1 ^= 1; memset(f1[now1], 0, sizeof(f1[now1]));
			for(int j = 0; j <= i; j++) {
				for(int k = 0; k < 11; k++) {
					f1[now1][j][k] += f1[now1 ^ 1][j][(k - b[i] + 11) % 11];
					if(j) (f1[now1][j][k] += f1[now1 ^ 1][j - 1][(k + b[i]) % 11]) %= mod;
				}
			}
		} LL ans = 0;
		for(int k = 0; k < 11; k++) {
			LL h1 = f0[now0][n1 / 2][k] * jc[n1 / 2] % mod * jc[n1 - n1 / 2] % mod;
			for(int i = 0; i <= n2; i++) {
				LL h2 = f1[now1][i][(11 - k) % 11] * calc(n1 + 1 - (n1 + 1) / 2, n2 - i) % mod * calc((n1 + 1) / 2, i) % mod;
				(ans += h1 * h2 % mod) %= mod;
			}
		} printf("%lld\n", ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xgc_woker/article/details/82929506