<span>题解「Luogu6189 [NOI Online #1 入门组]跑步」</span>

整数拆分问题。


首先有一个dp的方法:

\(f_{i,j}\) 表示用小于等于 \(i\) 的正整数组合成 \(j\) 的方案数,则有转移:

\[f_{i,j}=f_{i-1,j}+f_{i,j-i} \]

前一项相当于不用 \(i\) 组成 \(j\) ,后一项表示使用了 \(i\)

用这个卡一卡能得到 \(70\) 分,再滚动一下就有 \(80\) 分。


\(n\) 达到 \(10^5\) 时,只用上面的做法就不行了。

再考虑一种dp:

\(g_{i,j}\) 表示将 \(i\) 拆分成 \(j\) 个正整数的方案数,则有转移:

\[g_{i,j}=g_{i-1,j-1}+g_{i-j,j} \]

前一项表示新拆出一个 \(1\) ,后一项表示 \(j\) 个数全部加 \(1\)

考虑根号分治:用第一种dp求出只用小于等于 \(\sqrt{n}\) 的正整数的方案数,将第二种dp魔改一下,求出将 \(i\) 拆分成 \(j\) 个大于等于 \(\sqrt{n}\) 的正整数的方案数。

魔改后 \(g_{i,j}\) 有转移:

\[g_{i,j}=g_{i-\sqrt{n},j-1}+g_{i-j,j} \]

前一项表示新拆出一个 \(\sqrt{n}\) ,后一项同上。


考虑如何合并答案:

枚举一个数 \(i\) ,将 \(n\) 分成两部分: \(i\)\(n-i\) 。将 \(i\) 拆分成小于 \(\sqrt{n}\) 的数,将 \(n-i\) 拆分成大于等于 \(\sqrt{n}\) 的数,则总方案数:

\[\text{Ans}=\sum_{i=1}^n(f_{i,\sqrt{n}-1}\times \sum_{j=0}^{\sqrt{n}}g_{n-i,j}) \]

采用根号分治的做法,计算 \(f\) 需要 \(O(n\sqrt{n})\) ,因为大于 \(\sqrt{n}\) 的数最多只会用 \(\sqrt{n}\),所以计算 \(g\) 也只需要 \(O(n\sqrt{n})\) ,统计答案需要 \(O(n\sqrt{n})\) ,总时间复杂度 \(O(n\sqrt{n})\)


\(\text{Code}:\)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 100005
#define BN 400
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;

template <typename T>
inline void read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	x*=f;
}

int n;
int p,g[maxn][BN+5],f[maxn];

int main()
{
	// freopen("P6189.in","r",stdin);
	read(n),read(p);
	int S=n/BN+1;
	f[0]=1;
	for(int i=1;i<BN;++i)
		for(int j=i;j<=n;++j)
			(f[j]+=f[j-i])%=p;
	g[0][0]=1;
	for(int i=0;i<=n;++i)
		for(int j=1;j<=S&&j<=i;++j)
		{
			if(i>=BN) (g[i][j]+=g[i-BN][j-1])%=p;
			if(i>=j) (g[i][j]+=g[i-j][j])%=p;
		}
	lxl ans=0;
	for(int i=0;i<=n;++i)
	{
		int res=0;
		for(int j=0;j<=S;++j)
			(res+=g[n-i][j])%=p;
		(ans+=1ll*f[i]*res%p)%=p;
	}
	printf("%lld\n",ans);
	return 0;
}

全部评论

相关推荐

不愿透露姓名的神秘牛友
07-10 12:10
点赞 评论 收藏
分享
06-27 12:54
已编辑
门头沟学院 Java
累了,讲讲我的大学经历吧,目前在家待业。我是一个二本院校软件工程专业。最开始选专业是觉得计算机感兴趣,所以选择了他。本人学习计算机是从大二暑假结束开始的,也就是大三开始。当时每天学习,我个人认为Java以及是我生活的一部分了,就这样持续学习了一年半,来到了大四上学期末,大概是在12月中旬,我终于找的到了一家上海中厂的实习,但我发现实习生的工作很枯燥,公司分配的活也不多,大多时间也是自己在自学。就这样我秋招末才找到实习。时间来到了3月中旬,公司说我可以转正,但是转正工资只有7000,不过很稳定,不加班,双休,因为要回学校参加答辩了,同时当时也是心高气傲,认为可以找到更好的,所以放弃了转正机会,回学校准备论文。准备论文期间就也没有投递简历。然后时间来到了5月中旬,这时春招基本也结束了,然后我开始投递简历,期间只是约到了几家下场面试。工资也只有6-7k,到现在我不知道该怎么办了。已经没有当初学习的心劲了,好累呀,但是又不知道该干什么去。在家就是打游戏,boss简历投一投。每天日重一次。26秋招都说是针对26届的人,25怎么办。我好绝望。要不要参加考公、考研、央国企这些的。有没有大佬可以帮帮我。为什么感觉别人找工作都是顺其自然的事情,我感觉自己每一步都在艰难追赶。八股文背了又忘背了又忘,我每次都花很长时间去理解他,可是现在感觉八股、项目都忘完了。真的已经没有力气再去学习了。图片是我的简历,有没有大哥可以指正一下,或者说我应该走哪条路,有点不想在找工作了。
码客明:太累了就休息一下兄弟,人生不会完蛋的
如果实习可以转正,你会不...
点赞 评论 收藏
分享
frutiger:逆天,我家就安阳的,这hr咋能说3k的,你送外卖不比这工资高得多?还说大厂来的6k,打发叫花子的呢?这hr是怎么做到说昧良心的话的
找工作时遇到的神仙HR
点赞 评论 收藏
分享
来个大佬救一下,为上投了都是石沉大海了,没实习经历的话怕秋招直接进不了面。什么实习这么难找,基本
心态爆炸了:现在正式的岗位都少,实习基本不咋招的,除了大厂,中小企业其实没那么多岗位需求,就算是有,大多都是招一两个廉价劳动力,同时,他们也会希望你一来就能干活的,没时间培训你,就让你了解公司的项目,你了解完就可以开始干活。再者是,很多低质量的实习其实用处没有那么大的。我去年也是找实习找到破防,最后去了一家深圳的小公司实习,工作对我来说很简单,甚至不如我在学校做的项目,秋招的时候,这段实习经历也并没有帮上什么忙,投递简历,依旧非常低的回复率。低回复率是常态,尤其是找实习,找不到,那就把重心放在优化自己的简历和项目,多看八股文,锻炼自己的面试能力,多看别人的面经,自己模拟面试,等秋招的时候,只要有那么寥寥几次,好好抓住那几次机会。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务