数拆分 & P6189 [NOI Online #1 入门组] 跑步
模拟赛的一道题目
"ppip看到了一个各元素均为正整数、长度为k、各元素之和为n的不降数列。他想要知道这种数列的个数,对998244853取模。"
事实证明,我的写法是:先垫一层
正解代码:
#include<bits/stdc++.h>
using namespace std;
const int mod = 998244853;
int f[2][100010],n,k,ans;
inline int add(int x,int y){ return x + y >= mod ? x + y - mod : x + y; }
inline void toadd(int &x,int y){ x = add(x,y); }
int main(){
scanf("%d%d",&n,&k);
if(n < k)return puts("0"),0;
else if(n == k)return puts("1"),0;
f[0][0] = 1;
for(int i = 1;i<=k;++i){
int now = (i&1),pre = (now^1);
for(int j = 0;j<=n;++j)f[now][j] = 0;
for(int j = i;j<=n;++j){
toadd(f[now][j],f[pre][j-1]);
toadd(f[now][j],f[now][j-i]);
}
}
printf("%d",f[k&1][n]);
return 0;
}
下面是数拆分的几种类型。
没有限定拆分的个数,可以直接完全背包:
f[0]=1;
for(int i=1;i<=k;i++)
for(int j=i;j<=n;j++)
f[j]=(f[j]+f[j-i])%mod;
printf("%lld\n",f[n]);
限定拆分个数:
// f[i][j] 表示选择了 i 个,总和为 j
f[0][0] = 1
for(int i=1;i<=k;i++)
for(int j=i;j<=n;j++)
f[i][j]=f[i-1][j-1]+f[i][j-i];
理解:每次要么多选一个
要求拆出来的数不重复:
g[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
g[i][j]=g[i-1][j-i]+g[i][j-i];
理解:必须选
有些感性。相对于每次的增量为
P6189 [NOI Online #1 入门组] 跑步。
这也是一道数拆分的题目。
本质上就是上面的那个完全背包。但是是考虑到
对于
题解。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现