CF856C Eleventh Birthday
CF856C Eleventh Birthday
终于过了QWQ
题目大意
就是给你n个数,然后求按任意顺序摆放形成的数中, 可以被11整除的有多少个
题解
有一点小学奥数基础的可以很容易发现
即一个数如果可以被11整除,那他的奇数位和偶数位之和是相等的
举几个个简单的例子
121
=
11
∗
11
奇
数
位
之
和
位
1
+
1
=
2
,
偶
数
位
之
和
位
2
,
相
等
,
所
以
这
个
数
可
以
被
11
整
除
121 = 11 * 11 \ \ \ 奇数位之和位1 + 1 = 2, 偶数位之和位2,相等,所以这个数可以被11整除
121=11∗11 奇数位之和位1+1=2,偶数位之和位2,相等,所以这个数可以被11整除
13112
=
1192
∗
11
奇
数
位
之
和
位
1
+
1
+
2
=
4
,
偶
数
位
之
和
位
3
+
1
=
4
,
相
等
,
所
以
这
个
数
可
以
被
11
整
除
13112 = 1192 * 11 \ \ \ 奇数位之和位1 + 1 + 2= 4, 偶数位之和位3 + 1 = 4,相等,所以这个数可以被11整除
13112=1192∗11 奇数位之和位1+1+2=4,偶数位之和位3+1=4,相等,所以这个数可以被11整除
反之如果和不相等即不能被11整除
可以把问题转换为,奇数位的数字贡献位正的,偶数的贡献位负,然后加起来为0的即是11的倍数
然后我们可以把所有的数按照 奇数位数 的和 偶数位数 的分类‘
发现可以分开考虑,因为偶数位的插在两个奇数位中间不会影响到答案,前后的奇偶性还是没有变
然后设位数位奇数位的数的贡献为
a
[
1...
n
1
]
a[1...n1]
a[1...n1],位数为偶数位的数的贡献为
b
[
1...
n
2
]
b[1...n2]
b[1...n2]
先处理位数为奇数位的情况
假设第一位是奇数位,奇数位开始的贡献为正,偶数位开始的贡献为负
设 f [ i ] [ j ] [ k ] 表 示 前 i 个 位 数 为 奇 数 位 的 数 , 有 j 个 数 的 开 头 是 偶 数 位 , 贡 献 为 k 的 个 数 f[i][j][k]表示前i个位数为奇数位的数,有j个数的开头是偶数位,贡献为k的个数 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]] 易得f[i][j][k]=f[i−1][j][k−a[i]]+f[i−1][j−1][k+a[i]]
位数为偶数的同理,
g [ i ] [ j ] [ k ] = g [ i − 1 ] [ j ] [ k − b [ i ] ] + g [ i − 1 ] [ j − 1 ] [ k + b [ i ] ] g[i][j][k] = g[i-1][j][k-b[i]] + g[i-1][j-1][k+b[i]] g[i][j][k]=g[i−1][j][k−b[i]]+g[i−1][j−1][k+b[i]]
然后就到了最恶心的合并
发现位数为奇数的只有 f [ n 1 ] [ n 1 / 2 ] [ 0...10 ] f[n1][n1/2][0...10] f[n1][n1/2][0...10]
这个应该很容易证明吧
首先枚举位数为偶数的开头是偶数位的个数i,贡献为j
g [ n 2 ] [ i ] [ j ] ∗ f [ n 1 ] [ n 1 / 2 ] [ ( 11 − j ) m o d    11 ] g[n2][i][j] * f[n1][n1/2][(11 - j) \mod 11] g[n2][i][j]∗f[n1][n1/2][(11−j)mod11]
奇偶性相同的数可以按照开头的位数是奇数还是偶数来互相交换,其他数的位数奇偶性不受影响
即 ( n 1 / 2 ) ! ∗ ( n 1 − n 1 / 2 ) ! ∗ i ! ∗ ( n 2 − i ! ) (n1/2)! * (n1 - n1/2)! * i! * (n2 - i!) (n1/2)!∗(n1−n1/2)!∗i!∗(n2−i!)
最后考虑怎么把位数为偶数的插进去
贡献为正的 n 2 − i n2 - i n2−i个只能插在 n 1 / 2 n1/2 n1/2个之后,或再第一个之前,即有 n 1 / 2 + 1 n1/2 + 1 n1/2+1个位置可以插
即把 n 2 − i n2-i n2−i个球放在 n 1 / 2 + 1 n1/2 + 1 n1/2+1个盒子里,盒子可以空着
设 p = n 2 − i , q = n 1 / 2 + 1 p = n2 - i, q = n1/2 + 1 p=n2−i,q=n1/2+1 即 C [ p + q ] [ q − 1 ] C[p+q][q - 1] C[p+q][q−1](C表示组合数)
偶数同理
然后把这一大坨东西乘起来求个和就行了
看代码吧
code:
#include<bits/stdc++.h>
#define int long long
#define N 2005
#define mod 998244353
using namespace std;
int n, a[N], b[N], n1, n2, f[2][N][13], g[2][N][13], c[N][N], pw[N], t;
int calc(int n, int m){
if(m == 0) return (n == 0);//注意边界
return pw[n] * c[n + m - 1][m - 1] % mod;
}
signed main(){
pw[0] = 1; c[0][0] = 1;
for(int i = 1; i <= 2000; i ++) pw[i] = pw[i - 1] * i % mod, c[i][0] = 1;//预处理阶乘和组合数
for(int i = 1; i <= 2000; i ++)
for(int j = 1; j <= 2000; j ++)
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
scanf("%lld", &t);
while(t --){
scanf("%lld", &n);
n1 = n2 = 0;
for(int i = 1; i <= n; i ++){
int x, p = 0;
scanf("%lld", &x);
for(int j = x; j; j /= 10) p ^= 1;
if(p) a[++ n1] = x % 11; else b[++ n2] = x % 11;//a表示位数为奇数的,b表示位数为偶数的,把它们的贡献处理出来
}
memset(f, 0, sizeof f), memset(g, 0, sizeof g);
f[0][0][0] = g[0][0][0] = 1;
for(int i = 1; i <= n1; i ++){//dp位数为奇数的情况
memset(f[i&1], 0, sizeof f[i&1]);
for(int j = 0; j <= i; j ++)
for(int k = 0; k < 11; k ++){
int p = (k - a[i] + 11) % 11, q = (k + a[i]) % 11;
f[i&1][j][k] = (f[i&1][j][k] + f[(i - 1)&1][j][p]) % mod;
if(j) f[i&1][j][k] = (f[i&1][j][k] + f[(i - 1)&1][j - 1][q]) % mod;
}
}
for(int i = 1; i <= n2; i ++){//dp位数为偶数的情况
memset(g[i&1], 0, sizeof g[i&1]);
for(int j = 0; j <= i; j ++)
for(int k = 0; k < 11; k ++){
int p = (k - b[i] + 11) % 11, q = (k + b[i]) % 11;
g[i&1][j][k] = (g[i&1][j][k] + g[(i - 1)&1][j][p]) % mod;
if(j) g[i&1][j][k] = (g[i&1][j][k] + g[(i - 1)&1][j - 1][q]) % mod;
}
}
int ans = 0;
for(int i = 0; i <= n2; i ++)
for(int j = 0; j < 11; j ++){
ans = (ans + g[n2&1][i][j] * f[n1&1][n1 / 2][(11 - j) % 11] % mod * pw[n1 / 2] % mod * pw[n1 - n1 / 2] % mod * calc(i, n1 - n1 / 2) % mod * calc(n2 - i, n1 / 2 + 1) % mod) % mod;//算贡献,上面写的
}
printf("%lld\n", ans);
}
return 0;
}
啊
坑点
要注意内存,需要滚掉一维