[NOIP2021] 数列
前言#
场切的题,我到现在才来补。
感觉自己做了挺多 题了,可以尝试自己做一下。
大意#
给你一个长 的数组 ,要求满足 的 数列的权值和。定义一个数列的权值为 。
Sol#
看到数据范围很小,考虑高维 或者状压。显然状压没有前途,考虑设高维的 。
然后你考虑题目实际上是让你对于一个空白的二进制位,每次可以把一位加一,求最后 的个数不超过 个。我们必须要维护的状态显然有当前已经填了几个 ,然后当前 的个数是多少。这样我们当前加了一个位置之后,所需要知道的是能进几位的问题对吧。所以我们还需要维护当前进了几位,也就是前面填了的 有几个是因为进位而消失的。
这样我们有一个初步的想法就是令 表示当前填了 个 ,当前有 个 的权值,这样不见了的 就是 个。
考虑转移,就是枚举当前填的 是在哪个位置上的,然后我们枚举它进的位数,这样容易知道上一个状态的 的个数。然后你发现这点状态是不够的,你枚举当前进了几位,就必须要知道有连续几个 在当前位的答案。于是你发现这样的做法好像没有办法继续维护了。
那么既然是和位数相关,那我不妨从低位向高位去考虑。那由于这样会对计数没有顺序的过程产生影响,所以我们不妨顺序考虑二进制中的位,然后看当前这位填到 的哪一位去。就是说,我们把 表示当前考虑到二进制第 位,当前有 个 ,并且已经填了 中的 个数的权值和。接着考虑从当前状态转移出去,就是你考虑当前位放几个 ,假设是 个,那么对 的影响是加上了 ,当然还要加上上一位对当前位的贡献,所以还需要加上上一位的进位。
因此我们最终确定大炮状态是 表示考虑二进制第 位的时候,此前已经有了 个 ,并且填了 中 个数,且上一位对当前位的进位为 的所有方案的权值和。那么转移就比较好想了,就是:
然后暴力转移,最终答案就是 的和。
哦注意最后那个 的时候才可以统计入答案。
然后就做完了。
Code#
// Problem: P7961 [NOIP2021] 数列
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P7961
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// Time: 2022-04-13 21:42:27
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
using namespace std;
const int MOD=998244353;
int ksm(int a,int p){
int ret=1;while(p){
if(p&1) ret=ret*a%MOD;
a=a*a%MOD; p>>=1;
}return ret;
}
int C[35][35];
void init(){
C[0][0]=1;
rep(i,1,33){
C[i][0]=1; rep(j,1,i)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
}
}
int dp[110][110][35][35],v[110];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
init();
int n,m,K;cin>>n>>m>>K;
rep(i,0,m) cin>>v[i];
rep(p,0,n) dp[0][p][p][0]=C[n][p]*ksm(v[0],p)%MOD;
rep(i,0,m) rep(j,0,i) rep(k,0,n) rep(d,0,k){
// pt(i);pt(j);pt(k);pts(d);
for(int p=0;p+k<=n;p++){
// pt(dp[i][j][k][d]);pts(C[n-k][p]);
// pts(v[i]);
(dp[i+1][j+(p+d)%2][k+p][(p+d)/2]+=dp[i][j][k][d]*C[n-k][p]%MOD*ksm(v[i],p)%MOD)%=MOD;
}
}int ans=0;
rep(j,0,K) rep(d,0,n){
if(j+__builtin_popcount(d)>K) continue;
ans=(ans+dp[m+1][j][n][d])%MOD;
}cout<<ans<<'\n';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通