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