LOJ6089 小Y的背包计数问题(根号优化背包)

LOJ6089 小Y的背包计数问题(根号优化背包)

LOJ6089 小Y的背包计数问题(根号优化背包)

Solutioon

这道题利用根号分治可以把复杂度降到n根号n级别。

我们发现当物品体积大与根号n时,就是一个完全背包,换句话说就是没有了个数限制。

进一步我们发现,这个背包最多只能放根号n个物品。

所以我们设dp[i][j]表示放了i个物品,体积为j时的方案数。

转移的话一种是往背包里放一个新物品,或者让背包里所有物品体积加1.

当物品体积小于根号n时,因为物品个数比较少,所以我们可以设计状态为dp[i][j]表示前i个物品,占用j的体积为j时的方案数。

然后我们发现它的同类转移点是在模i的剩余系下是相等的,所以我们按照余数分组dp一下。

code

#include<iostream>
#include<cstdio>
#include<cmath>
#define N 100002
using namespace std;
typedef long long ll;
const int mod=23333333;
int f[317][N],g[317][N],s[N],sum[N],ji[N],ans;
int n,n1;
int main(){
    scanf("%d",&n);n1=sqrt(n);
    for(int i=1;i<=n1;++i)
    g[0][0]=1;s[0]=1;
    for(int i=1;i<=n1;++i)
      for(int j=0;j<=n;++j){
        if(j>=i)(g[i][j]+=g[i][j-i])%=mod;
        if(j>=n1+1)(g[i][j]+=g[i-1][j-n1-1])%=mod;
        (s[j]+=g[i][j])%=mod;
    } 
    f[0][0]=1;
    for(int i=1;i<=n1;++i)
      for(int j=0;j<i;++j){
          int tot=0;
          for(int k=j;k<=n;k+=i){
              ji[++tot]=f[i-1][k];
              sum[tot]=(sum[tot-1]+ji[tot])%mod;
              (f[i][k]+=(sum[tot]-sum[max(0,tot-i-1)]+mod))%=mod;
          }
      }
    for(int i=0;i<=n;++i)(ans+=1ll*s[i]*f[n1][n-i]%mod)%=mod;
    cout<<ans;
    return 0;
}