分块,对于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 }