当前位置: 代码迷 >> 综合 >> 51nod 1201 整数划分 1259 整数划分 V2
  详细解决方案

51nod 1201 整数划分 1259 整数划分 V2

热度:48   发布时间:2023-10-29 06:27:40.0

前言

今天在家颓了一天。。
为了证明自己还是有点用的,所以来补一篇博客
然而还是改不了今天颓了一天的事实

明天就要回去上文化课了。。什么都不会呢
感觉省选GG以后一连颓了很多天呢
好吧,省选的时候也在颓

1201 整数划分

将N分为若干个不同整数的和,有多少种不同的划分方式,例如:n = 6,{6} {1,5} {2,4} {1,2,3},共4种。由于数据较大,输出Mod 10^9 + 7的结果即可。

我们发现到,他所要用到的数,级别是根号的
于是我们可以设计DP
f[i][j]f[i][j]表示用了i个数,和为j的方案
转移比较巧妙
为了不重复计算
我们认为规定,我们最后搞出来的序列是单调下降的
然后有两种转移,第一种是使得这ii个数全部 + 1 ,第二种是,在让这ii个数全部加一后在末尾添加一个新的数 1
容易知道,这样是可以不重不漏地构造出所有方案的
于是这题就解决了
CODE:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;
const LL N=50005;
LL f[321][N];
int main()
{LL n;scanf("%lld",&n);f[0][0]=1;LL ans=0;for (LL u=1;u<=320;u++) {for (LL i=u;i<=n;i++)//用了u个数,凑出i有多少种方案 {f[u][i]=(f[u-1][i-u]+f[u][i-u])%MOD;}ans=(ans+f[u][n])%MOD;}printf("%lld\n",ans);return 0;
}

1259 整数划分 V2

由于这题的数字可以重复,所以上面那个做法就GG了。。
但是思路还是可以用的
我们考虑分块DP
我们可以DP两个东西
一个是使用n??n以内的数,凑出ii的方案 f [ i ]
一个是使用n??n以外的数,凑出ii的方案 g [ i ]
然后两个东西卷积起来,nn的位置就是答案了
对于前者,我们可以直接背包DP
然后后者,我们可以用上面那题的方法来做
因为他用的个数也是根号级别的
然后只需要把每一次补一个 1 ,改为补一个n??+1n+1就可以了

CODE:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;
const LL N=50005;
LL f[2][N];//用了前i个数,和为j的方案 
LL g[2][N];
LL h[N];
LL n,m;
int main()
{scanf("%lld",&n);m=sqrt(n);f[0][0]=1;int now=0,now1=0;for (LL u=1;u<=m;u++){now^=1;for (LL i=0;i<u;i++) f[now][i]=f[now^1][i];for (LL i=u;i<=n;i++) f[now][i]=(f[now][i-u]+f[now^1][i])%MOD;}m++;g[0][0]=1;h[0]=1;for (LL u=1;u<=m;u++){now1^=1;for (LL i=0;i<=n;i++){g[now1][i]=0;if (i>=m) g[now1][i]=(g[now1][i]+g[now1^1][i-m])%MOD;//假如一个根号nif (i>=u) g[now1][i]=(g[now1][i]+g[now1][i-u])%MOD;//全部数+1h[i]=(h[i]+g[now1][i])%MOD;}}LL ans=0;for (LL u=0;u<=n;u++)   ans=(ans+f[now][u]*h[n-u]%MOD)%MOD;printf("%lld\n",ans);return 0;
}