Live2D

【NOIP2021 T2】数列 (sequence) 题解

【NOIP2021 T2】数列

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

输出到文件 sequence.out 中。
仅一行一个整数,表示所有合法序列的权值和对 998244353 取模的结果。

Sample Input

5 1 1
2 1

Sample Output

40

在这里插入图片描述
【样例 2】
见选手目录下的 sequence/sequence2.in 与 sequence/sequence2.ans。

Solution

观察题目,我们发现\(a[i]\)的顺序不同对答案的贡献是相同的,所以我们考虑顺序放置\(a[i]\),然后利用组合数求出全部方案,于是这题就可以进行\(DP\)

以下我们规定关于位数的全是在二进制下讨论,二进制下最低位是第0位

\(f[i][j][k][l]\)表示已经填完了前\(i\)个数,当前进位进到的最高位是第\(j\)位(也就是说之前的\(a[i]\)最大填到\(j-1\),即整数\(S\)最高在第\(j-1\)位填了1),\(a[i]\)中填了\(k\)\(j\),已经有\(l\)个位是1的合法序列的权值总贡献

考虑转移,我们可以用已知的\(f[i][j][k][l]\)向其他状态转移,我们假设在\(a[i]\)中填\(num\)\(j\),那么转移方程如下:

\(f[i+num][j+1][(k+num)/2][l+(k+num)\%2]+=f[i][j][k][l]*v_{j}^{num}*C_{i+num}^{num}\)

其中\(i+num\)表示填了这\(num\)个数后一共填完了多少个\(a[i]\)\(j+1\)表示往下进了一位(就算没有进位也可以默认进了位,因为这是没有影响的),\((k+num)/2\)表示向下一位进了几个1,\((k+num)\%2\)就是判断这个位的数是1还是0,然后再乘上一个组合数

最后答案即为\(\sum {f[n][m+1][k][l]} (count(k)+l≤K)\),其中\(count(k)\)表示数字\(k\)在二进制下有多少个位为1,\(K\)是题目中输入的\(k\)(其实就是处理最高位的进位)

CODE

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 35, M = 105;
const LL MO = 998244353;
int n, m, K;
LL v[M][N], f[N][M][N][N], C[N][N], ans;
int lowbit(int x) {
	return x & (-x);
}
int count(int x) {
	LL res = 0;
	for (; x; x -= lowbit(x)) ++res;
	return res;
}//统计一个数在二进制下有多少位是1
int main() {
	freopen("sequence.in", "r", stdin); freopen("sequence.out", "w", stdout);//文件输入输出
	C[0][0] = 1;
	for (int i = 1; i < N; ++i) {
		C[i][0] = 1;
		for (int j = 1; j <= i; ++j)
			C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MO;
	}//预处理组合数
	scanf("%d%d%d", &n, &m, &K);
	for (int i = 0; i <= m; ++i) {
		LL vv; scanf("%lld", &vv); v[i][0] = 1;
		for (int j = 1; j <= n; ++j) v[i][j] = v[i][j - 1] * vv % MO;
	}//预处理v[i]的幂
	f[0][0][0][0] = 1;
	for (int i = 0; i <= n; ++i)
		for (int j = 0; j <= m; ++j)
			for (int k = 0; k <= i / 2; ++k)
				for (int l = 0; l <= K; ++l)
					if (f[i][j][k][l])
						for (int num = 0; num <= n - i; ++num)
							f[i + num][j + 1][k + num >> 1][l + (k + num & 1)] = (f[i + num][j + 1][k + num >> 1][l + (k + num & 1)] + f[i][j][k][l] * v[j][num] % MO * C[i + num][num] % MO) % MO;//转移
	for (int k = 0; k <= n; ++k)
		for (int l = 0; l <= K; ++l)
			if (count(k) + l <= K) ans = (ans + f[n][m + 1][k][l]) % MO;//统计答案
	printf("%lld\n", ans);
	return 0;
}
posted @ 2021-11-27 15:01  冷笑叹秋萧  阅读(250)  评论(0编辑  收藏  举报