LOJ #6089. 小 Y 的背包计数问题
神仙题啊orz。
首先把数分成(<=sqrt n)的和(>sqrt n)的两部分。
(>sqrt n)的部分因为最多选(sqrt n)个数,所以数量就没有卵用了。然后就用完全背包的一个常见套路(?)可以对一个空的序列整体+1或者在最左边加上一个(sqrt n+1),这个操作序列和完全背包的选择方案一一对应。感性理解一下是对的emmmm,复杂度(O(nsqrt n))
(<=sqrt n)的部分只有(sqrt n)个数,就可以多重背包做,然后用剩余系优化。
剩余系就是说多重背包方案的转移方程是(f[i][j]=sum_{k=1}^{i}f[i-1][j-ki])这个样子的
可以发现转移过来的j都和原来的j同余(( ext{mod } i))
对于每一个( ext{mod }i)的余数做一个f的前缀和,转移过来的一定是连续的一段
orz
#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il int gi(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
#define mod 23333333
ll f[320][100010];
ll h[100010],s[100010];
int main(){
int n=gi(),m=int(sqrt(n));
f[0][0]=1;s[0]=1;
for(int i=1;i<=m;++i)
for(int j=0;j<=n;++j){
if(j>i)f[i][j]+=f[i][j-i];
if(j>m)f[i][j]+=f[i-1][j-m-1];
f[i][j]%=mod;
s[j]=(s[j]+f[i][j])%mod;
}
memset(f,0,sizeof f);f[0][0]=1;
for(int i=1;i<=m;++i){
for(int j=0;j<i;++j){
int t=0;
for(int k=j;k<=n;k+=i)h[++t]=f[i-1][k];
for(int k=2;k<=t;++k)h[k]=(h[k]+h[k-1])%mod;
for(int k=j,tot=0;k<=n;k+=i){
++tot;
f[i][k]=(f[i][k]+h[tot]-h[std::max(0,tot-i-1)]+mod)%mod;
}
}
}
ll ans=0;
for(int i=0;i<=n;++i)ans+=s[i]*f[m][n-i]%mod;
printf("%lld
",ans%mod);
return 0;
}