
此题看上去直接暴力dfs,你就可以拿到20pts
但是优化一下,设dp数组dp[pos][num]表示搜索到第pos位,总和为num时的余下位的权值进行一下记忆化搜索,就可以水到50pts
#include<bits/stdc++.h>
#define re register
#define int long long
#define inl inline
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return sum*f;
}
const int mod=998244353;
inl int count(int x){
int res=0;
while(x){
res++;
x-=(x&-x);
}
return res;
}
int n,m,k;
int v[110],a[1000],ans;
int dp[35][120010];
inl int dfs(int pos,int num){
if(pos==n+1) return count(num)<=k;
if(dp[pos][num]!=-1) return dp[pos][num];
int res=0;
for(re int i=0;i<=m;i++){
a[pos]=i;
res=(res+dfs(pos+1,num+(1<<i))*v[i]%mod)%mod;
}
return dp[pos][num]=res;
}
signed main(){
n=read(),m=read(),k=read();
memset(dp,-1,sizeof(dp));
for(re int i=0;i<=m;i++) v[i]=read();
printf("%lld",dfs(1,0));
return 0;
}
下面开始讲正解,其实是dp,设dp[i][j][p][q],表示前i位填了j个1,i+1位堆积了p个1,前i位共有q个1的答案
可以得到转移:dp[i][j][p][q]=dp[i-1][j-d][a][b]*(val[i]^d)*
d表示第i位填的1的个数,val[i]^d表示d个a[i]带来的贡献
表示a的组合情况
ab满足下列等式:
(a+d)/2=p
b+[(a+d)%2]=q
那么答案就为
Code
#include<bits/stdc++.h>
#define re register
#define int long long
#define inl inline
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return sum*f;
}
const int mod=998244353;
inl int count(int x){
int res=0;
while(x){
res++;
x-=(x&-x);
}
return res;
}
int n,m,k;
int v[110],a[1000],ans;
int dp[110][35][35][35];//放到i位,j个1,i+1堆了p个1,到i有q个合法1
inl int ksm(int a,int b){
int res=1;
while(b){
if(b&1) res=res*a%mod;
b>>=1;a=a*a%mod;
}
return res;
}
int c[35][35];
inl void init(){
for(re int i=0;i<=n;i++){
c[i][0]=1;
for(re int j=1;j<=i;j++){
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
return;
}
//int dp[35][120010];
/*inl int dfs(int pos,int num){
if(pos==n+1) return count(num)<=k;
if(dp[pos][num]!=-1) return dp[pos][num];
int res=0;
for(re int i=0;i<=m;i++){
a[pos]=i;
res=(res+dfs(pos+1,num+(1<<i))*v[i]%mod)%mod;
}
return dp[pos][num]=res;
}*/
signed main(){
n=read(),m=read(),k=read();
for(re int i=0;i<=m;i++) v[i]=read();
init();
for(re int i=0;i<=n;i++){
dp[0][i][i/2][i&1]=ksm(v[0],i)*c[n][i]%mod;
}
for(re int i=1;i<=m;i++){
for(re int j=0;j<=n;j++){
for(re int p=0;p<=j/2;p++){
for(re int q=0;q<=j;q++){
for(re int d=0;d<=j;d++){
int a=p*2-d;
int b=q-(a+d)%2;
if(a>=0&&b>=0){
dp[i][j][p][q]+=dp[i-1][j-d][a][b]*ksm(v[i],d)%mod*c[n-(j-d)][d]%mod;
}
dp[i][j][p][q]%=mod;
a++;
b=q-(a+d)%2;
if(a>=0&&b>=0){
dp[i][j][p][q]+=dp[i-1][j-d][a][b]*ksm(v[i],d)%mod*c[n-(j-d)][d]%mod;
}
dp[i][j][p][q]%=mod;
}
}
}
}
}
for(re int i=0;i<=n;i++){
for(re int j=0;j<=k-count(i);j++){
ans=(ans+dp[m][n][i][j])%mod;
}
}
printf("%lld\n",ans);
return 0;
}
这篇博客探讨了一种动态规划(DP)的方法来解决位运算计数问题,涉及记忆化搜索优化和复杂状态转移方程。博主首先介绍了暴力DFS解法,然后详细解释了如何使用二维DP优化到50分的解决方案。接着,博主给出了完整的三维DP正解,通过计算前i位填了j个1,i+1位堆积了p个1,以及前i位共有q个1的答案。博客中还提供了两种不同实现的代码示例,并对代码进行了详细解释。最后,博主强调了理解和设置状态转移方程的重要性。

1190

被折叠的 条评论
为什么被折叠?



