10086

my first title

my first paragraph

数拆分 & P6189 [NOI Online #1 入门组] 跑步

模拟赛的一道题目

"ppip看到了一个各元素均为正整数、长度为k、各元素之和为n的不降数列。他想要知道这种数列的个数,对998244853取模。"

事实证明,我的写法是:先垫一层 1,再用拆解为正整数的做法去求答案。虽然过了,但感觉自己都不知道自己在干什么。

正解代码:

#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];

理解:每次要么多选一个 1,要么每个数都 +1

要求拆出来的数不重复:

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];

理解:必须选 i,每次要么多放一个 i,要么每个数都加上 i
有些感性。相对于每次的增量为 1 而言,每次增量为 i 可以保证每个增量都会出现

P6189 [NOI Online #1 入门组] 跑步

这也是一道数拆分的题目。
本质上就是上面的那个完全背包。但是是考虑到 x>n 的个数 <n,采用根号分治解决。
对于 x<n 的部分使用第一类,x>n 的采用第二类,最后合并即可。时间复杂度 O(nn)
题解

posted @   Luzexxi  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示