题解:[NOIP2021] 数列
DP
状态设计
为了便于处理,称二进制最低位为第 \(0\) 位(权值为 \(2^0\))。
考虑从小到大填入 \(a_i\)。
设 \(dp_{i,j,k,l}\) 表示处理了 \(a_1\sim a_i\),\(S\) 能进位到的最高位为 \(2^j\)(实际上最高位也可以不是 \(2^j\),之前的 \(a_i\) 最大值为 \(j-1\)),最高位 \(2^j\) 有 \(k\) 个,已知 \(l\) 位为 \(1\) 的答案。
则答案为:
\[\sum_{\operatorname{count}(k)+l\leq K}dp_{n,m+1,k,l}
\]
其中,\(\operatorname{count}(k)\) 表示 \(k\) 在二进制下 \(1\) 的个数。
状态转移
不好从其他状态转移到 \(dp_{i,j,k,l}\),那就考虑从 \(dp_{i,j,k,l}\) 向外转移。
假设再往 \(a\) 中填入 \(t\) 个 \(j\),则有:
\[dp_{i+t,j+1,\left\lfloor\frac{k+t}{2}\right\rfloor,l+(k+t)\bmod 2}\leftarrow dp_{i+t,j+1,\left\lfloor\frac{k+t}{2}\right\rfloor,l+(k+t)\bmod 2}+dp_{i,j,k,l}\cdot v_j^t\cdot \binom{i+t}{t}
\]
再往 \(a\) 中填入 \(t\) 个 \(j\),则之后一共有 \(i+t\) 个;\(S\) 进位到了第 \(j+1\) 位;\(\left\lfloor\dfrac{k+t} {2}\right\rfloor\) 表示向下一位进的 \(1\) 的个数;\(l+(k+t)\bmod 2\) 是进位后剩下的没有进位的 \(1\) 与原来的 \(2^0\sim 2^{j-1}\) 的 \(l\) 个 \(1\) 的总共的数量。
然后再乘上一个组合数即可,从 \(i+t\) 个中选择 \(t\) 个填入。
边界条件
\[dp_{0,0,0,0}=1
\]
AC 代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
#include<unordered_map>
using namespace std;
constexpr const int N=30,M=100,K=N,P=998244353;
int n,m,kk,v[M+1];
int dp[N+1][M+1+1][N+1][K+1];
int qpow(int a,int n){
int base=a,ans=1;
while(n){
if(n&1){
ans=1ll*ans*base%P;
}
base=1ll*base*base%P;
n>>=1;
}
return ans;
}
int C(int n,int m){
static int mem[N+M+1][N+1];
if(n<m){
return 0;
}
if(m==0||m==n){
return 1;
}
if(mem[n][m]){
return mem[n][m];
}
return mem[n][m]=(1ll*C(n-1,m)+C(n-1,m-1))%P;
}
int count(int x){
int ans=0;
while(x){
if(x&1){
ans++;
}
x>>=1;
}
return ans;
}
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m>>kk;
for(int i=0;i<=m;i++){
cin>>v[i];
}
dp[0][0][0][0]=1;
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
for(int k=0;k<=n;k++){
for(int l=0;l<=kk;l++){
if(dp[i][j][k][l]){
for(int t=0;i+t<=n;t++){
int &pl=dp[i+t][j+1][k+t>>1][l+(k+t&1)];
pl=(1ll*pl+1ll*dp[i][j][k][l]*qpow(v[j],t)%P*C(i+t,t)%P)%P;
}
}
}
}
}
}
int ans=0;
for(int k=0;k<=n;k++){
for(int l=0;l<=kk;l++){
if(count(k)+l<=kk){
ans=(1ll*ans+dp[n][m+1][k][l])%P;
}
}
}
cout<<ans<<'\n';
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}
/*
8 9 4
934258593 150407625 187068439 162292791 219945760 512449588 803393963 983648121 484675481 412407699
642171527
*/