P7961 [NOIP2021] 数列

23 篇文章 0 订阅

[NOIP2021] 数列

题目描述

给定整数 n , m , k n, m, k n,m,k,和一个长度为 m + 1 m + 1 m+1 的正整数数组 v 0 , v 1 , … , v m v_0, v_1, \ldots, v_m v0,v1,,vm

对于一个长度为 n n n,下标从 1 1 1 开始且每个元素均不超过 m m m 的非负整数序列 { a i } \{a_i\} {ai},我们定义它的权值为 v a 1 × v a 2 × ⋯ × v a n v_{a_1} \times v_{a_2} \times \cdots \times v_{a_n} va1×va2××van

当这样的序列 { a i } \{a_i\} {ai} 满足整数 S = 2 a 1 + 2 a 2 + ⋯ + 2 a n S = 2^{a_1} + 2^{a_2} + \cdots + 2^{a_n} S=2a1+2a2++2an 的二进制表示中 1 1 1 的个数不超过 k k k 时,我们认为 { a i } \{a_i\} {ai} 是一个合法序列。

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

输入格式

输入第一行是三个整数 n , m , k n, m, k n,m,k

第二行 m + 1 m + 1 m+1 个整数,分别是 v 0 , v 1 , … , v m v_0, v_1, \ldots, v_m v0,v1,,vm

输出格式

仅一行一个整数,表示所有合法序列的权值和对 998244353 998244353 998244353 取模的结果。

样例 #1

样例输入 #1

5 1 1
2 1

样例输出 #1

40

样例 #2

样例输入 #2

见附件中的 sequence/sequence2.in

样例输出 #2

见附件中的 sequence/sequence2.ans

提示

【样例解释 #1】

由于 k = 1 k = 1 k=1,而且由 n ≤ S ≤ n × 2 m n \leq S \leq n \times 2^m nSn×2m 知道 5 ≤ S ≤ 10 5 \leq S \leq 10 5S10,合法的 S S S 只有一种可能: S = 8 S = 8 S=8,这要求 a a a 中必须有 2 2 2 0 0 0 3 3 3 1 1 1,于是有 ( 5 2 ) = 10 \binom{5}{2} = 10 (25)=10 种可能的序列,每种序列的贡献都是 v 0 2 v 1 3 = 4 v_0^2 v_1^3 = 4 v02v13=4,权值和为 10 × 4 = 40 10 \times 4 = 40 10×4=40

【数据范围】

对所有测试点保证 1 ≤ k ≤ n ≤ 30 1 \leq k \leq n \leq 30 1kn30 0 ≤ m ≤ 100 0 \leq m \leq 100 0m100 1 ≤ v i < 998244353 1 \leq v_i < 998244353 1vi<998244353

测试点 n n n k k k m m m
1 ∼ 4 1 \sim 4 14 = 8 =8 =8 ≤ n \leq n n = 9 =9 =9
5 ∼ 7 5 \sim 7 57 = 30 =30 =30 ≤ n \leq n n = 7 =7 =7
8 ∼ 10 8 \sim 10 810 = 30 =30 =30 ≤ n \leq n n = 12 =12 =12
11 ∼ 13 11 \sim 13 1113 = 30 =30 =30 = 1 =1 =1 = 100 =100 =100
14 ∼ 15 14 \sim 15 1415 = 5 =5 =5 ≤ n \leq n n = 50 =50 =50
16 16 16 = 15 =15 =15 ≤ n \leq n n = 100 =100 =100
17 ∼ 18 17 \sim 18 1718 = 30 =30 =30 ≤ n \leq n n = 30 =30 =30
19 ∼ 20 19 \sim 20 1920 = 30 =30 =30 ≤ n \leq n n = 100 =100 =100

Sol

表示当前考虑到第i位,已经放了j个数,当前已经有了k个1,并且这一位还存储的1的个数p
考虑从这一个位置开始向下转移,考虑枚举这一个位置该放置1的个数,然后向下转移
f [i+1][j+t][k+(t+p)%2][(t+p)/2]+= f[i][j][k][p]*C[n-j][t]*pow(a[i],t)%mod
考虑答案在什么时候出现,显然就是,f[m+1][n][0~k][p] 并且满足 p向上进位之后加上k还是小于输入的k

#include<bits/stdc++.h>
#define I inline
#define LL long long
#define inf 0x3f3f3f3f
#define LB long double
#define RI register int
#define N 32
#define debug puts("debug");
#define M 150
#define mod 998244353
#define memeory cout<<abs(&me2-&me1)/1024./1024<<"MB"<<endl;
using namespace std;
I LL read()
{
	LL res=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
	return res*f;
}
LL C[M][M],n,m,K,a[M],f[M][N][N][N];
//表示当前考虑到第i位,已经放了j个数,当前已经有了k个1,并且这一位还存储的1的个数p 
//考虑从这一个位置开始向下转移,考虑枚举这一个位置该放置1的个数,然后向下转移
//f [i+1][j+t][k+(t+p)%2][(t+p)/2]+= f[i][j][k][p]*C[n-j][t]*pow(a[i],t)%mod
//考虑答案在什么时候出现,显然就是,f[m+1][n][0~k][p] 并且满足 p向上进位之后加上k还是小于输入的k
I void getC(){
	for(RI i=0;i<=n;i++)C[i][0]=1;
	for(RI i=1;i<=n;i++)
	for(RI j=1;j<=i;j++)
	C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	
} 
I int pop(int x){
	int ans=0;while(x)ans+=(x&1),x>>=1;
	return ans;
}
I LL qpow(LL ds,LL zs){
	LL ans=1;while(zs){
		if(zs&1)ans=(ans*ds)%mod;
		ds=(ds*ds)%mod;zs>>=1;
	}return ans;
}
int main()
{
	n=read();m=read();K=read();getC();
	for(RI i=0;i<=m;i++)a[i]=read();f[0][0][0][0]=1;
	for(RI i=0;i<=m;i++){
		for(RI j=0;j<=n;j++){
			for(RI k=0;k<=K;k++){
				for(RI p=0;p<=n;p++){
					for(RI t=0;t+j<=n;t++){
						f[i+1][j+t][k+(t+p)%2][(t+p)/2]
						+=f[i][j][k][p]*C[n-j][t]%mod*qpow(a[i],t)%mod;
						f[i+1][j+t][k+(t+p)%2][(t+p)/2]%=mod;
					}
				}
			}
		}
	}
	LL num=0;for(RI k=0;k<=K;k++)for(RI p=0;p<=n;p++)if(k+pop(p)<=K)num+=f[m+1][n][k][p],num%=mod;
	printf("%lld\n",num%mod);return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值