[NOIP2021] 数列

题目描述#

给定整数 n,m,k,和一个长度为 m+1 的正整数数组 v0,v1,,vm

对于一个长度为 n,下标从 1 开始且每个元素均不超过 m 的非负整数序列 {ai},我们定义它的权值为 va1×va2××van

当这样的序列 {ai} 满足整数 S=2a1+2a2++2an 的二进制表示中 1 的个数不超过 k 时,我们认为 {ai} 是一个合法序列。

计算所有合法序列 {ai} 的权值和对 998244353 取模的结果。

数据范围#

对所有测试点保证 1kn300m1001vi<998244353

solution#

按位 dp

由低位向高位考虑 S

fi,j,k,p 表示讨论了 S 从低到高的前 i 位,已经确定了 j 个序列 a 中的元素,S 从低到高前 i 位中有 k1 ,要向当前讨论位的下一位进位 p

转移时枚举 ai 中有多少个 i

f(i+1, j+t, k+(t+p) mod 2, t+p2)=f(i, j, k, p)×vit×(njt)

注意还有 m 位后面的 1 也要统计

复杂度 O(n4m)

code#

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const ll mod = 998244353;

ll ans, v[105], f[105][35][35][16], fac[105][35];

ll C[35][35];
inline void init(int n) {
	for (int i = 0; i <= n; i++) C[i][0] = 1;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= i; j++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}

inline int popcnt(int n) {
	int res = 0;
	while (n) res += n & 1, n >>= 1;
	return res;
}

int main() {
	init(30);
	int n, m, K;
	scanf("%d %d %d", &n, &m, &K);
	for (int i = 0; i <= m; i++) {
		scanf("%lld", &v[i]);
		fac[i][0] = 1;
		for (int j = 1; j <= n; j++) fac[i][j] = fac[i][j - 1] * v[i] % mod;
	}
	f[0][0][0][0] = 1;
	for (int i = 0; i <= m; i++)
		for (int j = 0; j <= n; j++)
			for (int k = 0; k <= K; k++)
				for (int p = 0; p <= n >> 1; p++)
					for (int t = 0; t <= n - j; t++) f[i + 1][j + t][k + (t + p & 1)][t + p >> 1] = (f[i + 1][j + t][k + (t + p & 1)][t + p >> 1] + f[i][j][k][p] * fac[i][t] % mod * C[n - j][t] % mod) % mod;
	for (int k = 0; k <= K; k++)
		for (int p = 0; p <= n >> 1; p++)
			if (k + popcnt(p) <= K) ans = (ans + f[m + 1][n][k][p]) % mod;
	printf("%lld", ans);
	return 0;
}
posted @   Dita  阅读(135)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
历史上的今天:
2021-10-25 CSP 赛后总结
点击右上角即可分享
微信分享提示
主题色彩