Description
你有一个大小为n的背包,你有n种物品,第i种物品的大小为i,且有i个,求装满这个背包的方案数有多少
两种方案不同当且仅当存在至少一个数i满足第i种物品使用的数量不同Input
第一行一个正整数n
1<=n<=10^5Output
一个非负整数表示答案,你需要将答案对23333333取模
Input1
3
Output1
2
Input2
233
Output2
1167892
DP,一开始没什么思路,然后研究了很久终于会做了,感觉我DP超弱
先是分成两部分,大于 n√ 和小于 n√ 的,小于的部分是有限制的背包,大于的部分是完全背包(因为你用不完),我们就可以分成两部分DP,两部分都不难,但是滚动再加上前缀和我就懵逼了,前面就是前缀和加滚动,后面是个序列DP。
前面dp[i][j]表示前i个数,体积为j,然后对于同一个i就是加上它的前缀和,然后滚一下i就好了
后面可以分为两种操作,一个是加一个m+1这个数,一个所有数加一
然后再乘法原理就好了
但是完全不知道为什么我原先写那个(注释里面的)在本机上测全部过,但是交上去就各种WA,样例都要WA,完全不知道为什么,是不是什么编译器的问题
然后下面是我的代码,注释掉的是我原来写的,注释后面的是网上抄的,完全不知道为什么我的交上去要WA
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<stack>
#define INF 2100000000
#define ll long long
#define clr(x) memset(x,0,sizeof(x))
#define maxclr(x) memset(x,127,sizeof(x))
#define N 320
#define M 100005
#define P 23333333LL
using namespace std;
int dp[2][M],tmp[M],g[N][M],n,m,ans;
ll g1[M],f1[M];
int now,pre;
int main()
{
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d",&n);
m=ceil(sqrt((double)n));
dp[0][0]=g[0][0]=1;
for(int i=0;i<=m;i++)
{
clr(tmp);
for(int j=0;j<=n;j++)
{
if(i)
{
int mod=j%i;
now=i&1;pre=1^(i&1);
tmp[mod]=(tmp[mod]+dp[pre][j])%P;
dp[now][j]=tmp[mod];
if(j>=i*i)
tmp[mod]=(tmp[mod]-dp[pre][j-i*i]+P)%P;
}
if(i&&j>=i)g[i][j]=(g[i][j]+g[i][j-i])%P;
if(j>m)g[i][j]=(g[i][j]+g[i-1][j-m-1])%P;
}
}
/*
for(int i=0;i<=m;i++)
for(int j=0;j<=n;j++)
ans=((ll)ans+(ll)dp[now][j]*(ll)g[i][n-j])%P;
*/
++g1[0];
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
(g1[i]+=g[j][i])%=P;
for (int i=0;i<=n;++i)f1[i]=dp[now][i];
for (int i=0;i<=n;++i)
(ans+=(f1[i]*g1[n-i]%P))%=P;
cout<<ans<<'\n';
return 0;
}
大概就是这个样子,如果有什么问题,或错误,请在评论区提出,谢谢。