[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 n≤S≤n×2m 知道 5 ≤ S ≤ 10 5 \leq S \leq 10 5≤S≤10,合法的 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 1≤k≤n≤30, 0 ≤ m ≤ 100 0 \leq m \leq 100 0≤m≤100, 1 ≤ v i < 998244353 1 \leq v_i < 998244353 1≤vi<998244353。
测试点 | n n n | k k k | m m m |
---|---|---|---|
1 ∼ 4 1 \sim 4 1∼4 | = 8 =8 =8 | ≤ n \leq n ≤n | = 9 =9 =9 |
5 ∼ 7 5 \sim 7 5∼7 | = 30 =30 =30 | ≤ n \leq n ≤n | = 7 =7 =7 |
8 ∼ 10 8 \sim 10 8∼10 | = 30 =30 =30 | ≤ n \leq n ≤n | = 12 =12 =12 |
11 ∼ 13 11 \sim 13 11∼13 | = 30 =30 =30 | = 1 =1 =1 | = 100 =100 =100 |
14 ∼ 15 14 \sim 15 14∼15 | = 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 17∼18 | = 30 =30 =30 | ≤ n \leq n ≤n | = 30 =30 =30 |
19 ∼ 20 19 \sim 20 19∼20 | = 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;
}