51nod3175 小Y的疑惑

3175 小Y的疑惑

小 Y 有一个大小为n的背包,并且小Y有n种物品。(n<=100000)

对于第i种物品,共有i个可以使用,并且对于每一个i物品,体积均为i。

求小Y把该背包装满的方案数为多少,答案对于23333333取模。

定义两种不同的方案为:当且仅当至少存在一种物品的使用数量不同。

输入

输入一个整数n。

输出

输出一行,表示方案数。

数据范围

对于10%的数据,满足 n<=10
对于30%的数据,满足 n<=1000
对于50%的数据,满足 n<=10000
对于100%的数据,满足n<=100000

输入样例

输入样例1:
3

输出样例

输出样例1:
2

解析:

显然直接背包dp用前缀和做可以做到n2n2

考虑令k=\sqrt{n}
对于前kk小暴力背包
后面[k+1,n]的显然怎么选都不会超过个数的限制
f[i][j]表示已经有i个数,和为j的方案数
考虑这样dp:
1、新加入一个大小为k+1的数
2、将之前ii个数每个都加1
显然这样是对的,且最多只有k个数

两部分复杂度都为n\sqrt{n}

放代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
int const maxn=1e5+5,maxm=320,mod=23333333;
int n,f[maxn],g[maxm][maxn],s[maxn],t[maxn],ans;
int main()
{
    scanf("%d",&n);  int sq=sqrt(n);
    f[0]=1;
    for(int i=1;i<=sq;i++)
    {
        for(int j=0;j<=n;j++)s[j]=(f[j]+(j>=i?s[j-i]:0))%mod;
        for(int j=0;j<=n;j++)
        {
            f[j]=s[j];
            if(j>=(i+1)*i)f[j]=(f[j]-s[j-(i+1)*i]+mod)%mod;
        }
    }
    g[0][0]=1;
    ans=f[n];//
    for(int i=1;i<=sq;i++)
        for(int j=i*(sq+1);j<=n;j++)//i*
        {
            g[i][j]=(g[i-1][j-sq-1]+g[i][j-i])%mod;
            ans=(ans+(ll)g[i][j]*f[n-j]%mod)%mod;
        }
    printf("%d\n",ans);
    return 0;
}

おすすめ

転載: blog.csdn.net/ZCH1901/article/details/120337320