紫色百合

https://nanti.jisuanke.com/t/16619

稍稍运用一下数学知识发现题目要求的是选出的集合每个元素+1之后的乘积等于2^P的方案数,取个log就变成了↓

在1~N选若干个数使得总和等于P,求方案数


证明:

设集合S的价值为f(S),当n=2时,设选了i和j号百合,集合S为{2^i-1,2^j-1},   f(S)=1(空集)+(2^i-1)+(2^j-1)+(2^i-1)*(2^j-1)=2^(i+j)

由递推关系

变形得

证毕。

然后用普通的背包DP可以就拿到60分了

    然后我们发现,由于物品大小是1~N,所以最多选取O(sqrt(P))个物品,背包就满了

满分做法可以用状态f[i][j]表示选i个物品,占容量为j的方案数

由于每个背包是不同的,所以根据已选的最小的物品分类讨论一下:

如果最小的物品是1,相当于i-1个物品凑出了j-i的大小,然后整体+1

如果最小的物品不是1,相当于i个物品凑出了j-i的大小,然后整体+1

需要注意我们要防止出现选择了大小为N+1的物品的情况,所以需要减去

得到递推式f[i][j]=f[i-1][j-i]+f[i][j-i]-f[i-1][j-(N+1)]

这个算法的精髓是巧妙地利用了所有物品的价值取遍了{1,...,n}中的所有自然数。

时间复杂度O(Nsqrt(N))

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=998244353;
int f[450][100010];
int main()
{
	int n,p;
	scanf("%d%d",&n,&p);
	int i,j,ans=0;
	f[0][0]=1;
	for(i=1;(i+1)*i/2<=p;i++)
	{
		for(j=i;j<=p;j++)
		{
			f[i][j]=f[i-1][j-i]+f[i][j-i];
			if(j>=(n+1)) f[i][j]-=f[i-1][j-(n+1)];
			if(f[i][j]<0) f[i][j]+=mod;
			if(f[i][j]>=mod) f[i][j]-=mod;
		}
		ans+=f[i][p];
		if(ans>=mod) ans-=mod;
	}
	printf("%d",ans);
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值