[51nod1597]有限背包计数问题

分块,对于sqrt(n)以内的点用二进制优化多重背包+前缀和来做,处理出f[i][j]表示前i种权值为j的方案数
对于sqrt(n)以外的点有两个性质:1.与数量无关且数量不超过sqrt(n);2.权值相邻;3.那么一定可以用两种操作来构造出来权值序列:1.加入一个sqrt(n)+1;2.将现有数+1(都不超过n),再用g[i][j]表示选了i个(不是种)权值和为j,可以转移到g[i][i+j]和g[i+1][j+sqrt(n)+1],最后将两个卷起来(题目说恰好为n,所以不用fft)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 100005
 4 #define mod 23333333
 5 int K,n,ans,f[N],sum[N],g[320][N];
 6 int main(){
 7     scanf("%d",&n);
 8     K=(int)sqrt(n);
 9     f[0]=g[0][0]=1;
10     for(int i=1;i<=K;i++){
11         for(int j=0;j<i;j++)sum[j]=f[j];
12         for(int j=i;j<=n;j++)sum[j]=(sum[j-i]+f[j])%mod;
13         for(int j=0;j<i*i+i;j++)f[j]=sum[j];
14         for(int j=i*i+i;j<=n;j++)f[j]=(sum[j]-sum[j-i*i-i]+mod)%mod;
15     }
16     for(int i=0;i<=K;i++)
17         for(int j=0;j<=n;j++){
18             if ((i)&&(i+j<=n))g[i][i+j]=(g[i][i+j]+g[i][j])%mod;
19             if (j+K<n)g[i+1][j+K+1]=(g[i+1][j+K+1]+g[i][j])%mod;
20         }
21     for(int i=1;i<=K;i++)
22         for(int j=0;j<=n;j++)g[i][j]=(g[i][j]+g[i-1][j])%mod;
23     for(int i=0;i<=n;i++)ans=(ans+1LL*g[K][i]*f[n-i])%mod;
24     printf("%d",ans);
25 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/11355851.html