JZOJ3056 数字 题解 容斥

题目描述

一个数字被称为好数字当他满足下列条件:

  • 它有 2 × n 2 \times n 2×n 个数位, n n n 是正整数(允许有前导0);
  • 构成它的每个数字都在给定的数字集合 S S S 中;
  • 它前 n n n 位之和与后 n n n 位之和相等或者它奇数位之和与偶数位之和相等。例如对于 n = 2 , S = { 1 , 2 } n=2,S=\{1,2\} n=2,S={1,2},合法的好数字有 1111 , 1122 , 1212 , 1221 , 2112 , 2121 , 2211 , 2222 1111,1122,1212,1221,2112,2121,2211,2222 1111,1122,1212,1221,2112,2121,2211,2222 这样8个。

已知 n n n,求合法的好数字的个数 mod 999983 999983 999983

输入格式

第一行一个数 n n n
接下来一个长度不超过 10 10 10 的字符串,表示给定的数字集合。

输出格式

一行一个数字表示合法的好数字的个数 mod 999983 999983 999983

样例

样例输入 #1

2
0987654321

样例输出 #1

1240

样例输入 #2

100
9765320

样例输出 #2

938457

提示

对于 20 20% 20 的数据, n ≤ 7 n \leq 7 n7
对于 100 100% 100 的.据, n ≤ 1000 , ∣ S ∣ ≤ 10 n \leq 1000,|S| \leq 10 n1000,S10

解题思路

前置知识:容斥原理

  • 构成它的每个数字都在给定的数字集合 S S S 中;
  • 它前 n n n 位之和与后 n n n 位之和相等或者它奇数位之和与偶数位之和相等。例如对于 n = 2 , S = { 1 , 2 } n=2,S=\{1,2\} n=2,S={1,2},合法的好数字有 1111 , 1122 , 1212 , 1221 , 2112 , 2121 , 2211 , 2222 1111,1122,1212,1221,2112,2121,2211,2222 1111,1122,1212,1221,2112,2121,2211,2222 这样8个。

分别为条件A、条件B。

由于要求的数可以满足两个条件 A、B 中的任意一个,可以先求出满足 A条件的个数,再求出满足 B 条件的个数,最后求出同时满足A、B的个数, A + B − A ∩ B A+B-A∩B A+BAB 就是答案。

  • 满足 A 条件:
    f i , j f_{i,j} fi,j 表示从集合 S S S 中取出的数,组成 i i i 位,和为 j j j 的方案数。
    递推式: f i , j = ∑ f i − 1 , j − S k f_{i,j} =\sum f_{i - 1,j - S_k} fi,j=fi1,jSk
    那么可以枚举前 n n n 位和为 x x x 的方案即所有的 f n , x f_{n,x} fn,x,由于要满足后 n n n 位与前 n n n 位和相等,那么后n位的方案数也是 f n , x f_{n,x} fn,x,两者相乘就是满足条件 A 的方案数,为 f n , x 2 f_{n,x}^2 fn,x2

  • 满足 B 条件:
    显然,偶数位上的所有数长度为 n n n,奇数位上的所有数长度也是 n n n。可以把奇数序列当做一个单独的序列,偶数序列当做一个单独的序列,那么方案数就和满足条件 A 的方案数一样了,也为 f n , x 2 f_{n,x}^2 fn,x2

  • 同时满足 A 条件与 B 条件:
    设该数列的奇数位序列为 X,偶数位序列为 Y。X 的前半段和与后半段和为分别为 x 1 x_1 x1 x 2 x_2 x2,Y 的前半段和与后半段和分别为 y 1 y_1 y1 y 2 y_2 y2。要求同时满足条件A与条件B,则 x 1 + x 2 = y 2 + y 2 x_1 + x_2= y_2 + y_2 x1+x2=y2+y2 x 1 + y 1 = x 2 + y 2 x_1 + y_1= x_2 + y_2 x1+y1=x2+y2,。X 的前半段和 Y 的后半段长度显然为 n + 1 2 \frac{n + 1}{2} 2n+1,所以我们可以枚举并累加所有的 f n + 1 2 , i 2 f_{\frac{n + 1}{2},i}^2 f2n+1,i2。偶数序列同理枚举累加所有的 f n 2 , i 2 f_{\frac{n}{2},i}^2 f2n,i2。所以这个并集的方案数为: ∑ f n + 1 2 , i 2 × ∑ f n 2 , i 2 \sum f_{\frac{n + 1}{2},i}^2 \times \sum f_{\frac{n}{2},i}^2 f2n+1,i2×f2n,i2

把总方案数减掉并集的方案数就是答案,即 A n s w e r = 2 × ∑ f n , i 2 − ∑ f n + 1 2 , i 2 × ∑ f n 2 , i 2 Answer=2 \times \sum f_{n,i}^2-\sum f_{\frac{n + 1}{2},i}^2 \times \sum f_{\frac{n}{2},i}^2 Answer=2×fn,i2f2n+1,i2×f2n,i2

AC Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
int n;
int F[1005][9005], S[15];
int Answer;
signed main() {
	FAST_IO;
	string s;
	cin >> n >> s;
	int length = s.size(), _max = -1;
	for (register int i = 1; i <= length; ++i) S[i] = s[i - 1] - '0', _max = max(_max, S[i]);
	F[0][0] = 1;
	for (register int i = 1; i <= n; ++i)for (register int j = 0; j <= i * _max; ++j)for (register int k = 1; k <= length; ++k)if (j - S[k] >= 0) F[i][j] = F[i][j] + F[i - 1][j - S[k]] % 999983, F[i][j] %= 999983;
	for (register int i = 0; i <= n * _max; ++i)Answer += 2 * F[n][i] * F[n][i], Answer %= 999983;
	int answer_1 = 0, answer_2 = 0;
	for (register int i = 0; i <= (n + 1) / 2 * _max; ++i) answer_1 += F[(n + 1) / 2][i] * F[(n + 1) / 2][i] % 999983, answer_1 %= 999983;
	for (register int i = 0; i <= n / 2 * _max; ++i) answer_2 += F[n / 2][i] * F[n / 2][i] % 999983, answer_2 %= 999983;
	Answer = Answer - answer_1 * answer_2 % 999983;
	cout << (Answer + 999983) % 999983 << endl;
	return 0;
}

小 F 了不起,大 F 夸自己。爆零就爆零,天天好心情。 小F了不起,大F夸自己。 爆零就爆零,天天好心情。 F了不起,大F夸自己。爆零就爆零,天天好心情。

  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值