LOJ #6089. 小 Y 的背包计数问题

法老上课讲的题,感觉海星就写了下,比较常规的设阈值+DP

首先我们考虑把物品分为两类,以(sqrt n)为阈值

(ile sqrt n)时,显然现在物品总数很少,我们可以直接枚举每个物品然后做多重背包

考虑使用多重背包的经典优化——完全背包差分,容易得出这部分的复杂度是(O(nsqrt n))

然后当(i>sqrt n)时,显然此时每个物品最多拿(frac{n}{i})个,而(i>frac{n}{i}),因此物品个数可以看做无限制

考虑用一个美妙的方法统计,设(f_{i,j})表示当前选了(i)个物品,体积和为(j)的方案数,转移操作有两种:

  • 新增一个物品,假定这个物品的体积为(sqrt n +1)
  • 将当前所有物品的体积都加(1)

不难发现这样的每个方案都和一种选取方法唯一对应,因此是正确的,同时因为最多选(sqrt n)个物品,复杂度是(O(nsqrt n))

综上,总体复杂度也是(O(nsqrt n))

#include<cstdio>
#include<cmath>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,M=330,mod=23333333;
int n,m,f1[N],f2[M][N],g[N],ans;
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
inline void dec(int& x,CI y)
{
	if ((x-=y)<0) x+=mod;
}
int main()
{
	RI i,j; for (scanf("%d",&n),m=sqrt(n),f1[0]=i=1;i<=m;++i)
	{
		for (j=i;j<=n;++j) inc(f1[j],f1[j-i]);
		for (j=n;j>=i*(i+1);--j) dec(f1[j],f1[j-i*(i+1)]);
	}
	for (f2[0][0]=1,i=0;i<=m;++i) for (j=0;j<=n;++j)
	{
		inc(g[j],f2[i][j]);
		if (j+m+1<=n) inc(f2[i+1][j+m+1],f2[i][j]);
		if (j+i<=n) inc(f2[i][j+i],f2[i][j]);
	}
	for (i=0;i<=n;++i) inc(ans,1LL*f1[i]*g[n-i]%mod);
	return printf("%d",ans),0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/13360068.html