http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1259
基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题
收藏
关注
将N分为若干个整数的和,有多少种不同的划分方式,例如:n = 4,{4} {1,3} {2,2} {1,1,2} {1,1,1,1},共5种。由于数据较大,输出Mod 10^9 + 7的结果即可。
Input
输入1个数N(1 <= N <= 50000)。
Output
输出划分的数量Mod 10^9 + 7。
Input示例
4
Output示例
5
与1201相似但是允许出现重复的数,如果还按照1201的写法,复杂度就是平方级了,看了讨论的解法感觉很巧妙。
利用分块将数据分成了[1,sqrt(n)],[sqrt(n)+1,n]两部分,分别用f[i][j]和g[i][j]表示用区间内的数j个组合成和为i的数的方案个数,计算f时无限背包,计算g时使用1201的
方程计算(注意这里的区间最小的值是sqrt(n+1))。最后用乘法原理计算答案ans=SUM{f[n][i]*h[n-i]},其中0<=i<=n,h[i]表示g[i][..]的总和。注意乘法时候会爆int使用longlong就好了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int mod=1e9+7; 4 #define LL long long 5 int f[50005],g[50005][330]; 6 int main() 7 { 8 int n,i,j,k; 9 cin>>n; 10 f[0]=1; 11 int m=sqrt(n*1.0); 12 for(i=1;i<=m;++i) 13 for(j=i;j<=n;++j) 14 f[j]=(f[j]+f[j-i])%mod; 15 16 g[0][0]=1; 17 g[m+1][1]=1; 18 for(i=m+1;i<=n;++i) 19 { 20 int k=i/(m+1); 21 for(j=1;j<=k;++j) 22 { 23 if(i+j<=n){ 24 g[i+j][j]=(g[i+j][j]+g[i][j])%mod; 25 } 26 if(i+m+1<=n){ 27 g[i+m+1][j+1]=(g[i+m+1][j+1]+g[i][j])%mod; 28 } 29 g[i][0]=(g[i][0]+g[i][j])%mod; 30 } 31 } 32 LL ans=0; 33 for(i=0;i<=n;++i) 34 ans=(ans+(LL)f[i]*g[n-i][0]%mod)%mod; 35 cout<<ans<<endl; 36 return 0; 37 }