紫色百合

来源:互联网 发布:ubuntu 杀死僵尸进程 编辑:程序博客网 时间:2024/04/27 13:30

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);}



原创粉丝点击