首
先
很
容
易
想
到
一
个
O
(
n
4
m
)
的
D
P
首先很容易想到一个O(n^4m)的DP
首先很容易想到一个O(n4m)的DP
设
d
p
[
i
]
[
j
]
[
q
]
为
长
度
i
,
a
数
组
以
j
结
尾
,
b
数
组
以
q
结
尾
(
q
>
=
j
)
设dp\ [i]\ [j]\ [q]\ 为长度i,a数组以j结尾,b数组以q结尾(q>=j)
设dp [i] [j] [q] 为长度i,a数组以j结尾,b数组以q结尾(q>=j)
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
dp[1][i][j]=1;//初始化长度为1的时候
for(int i=2;i<=m;i++)
for(int j=1;j<=n;j++)
for(int q=j;q<=n;q++)
for(int w=1;w<=j;w++)//升序
for(int e=q;e<=n;e++)//降序
dp[i][j][q]=(dp[i-1][w][e]+dp[i][j][q])%mod;
然 而 复 杂 度 炸 上 了 天 , 那 就 要 另 辟 蹊 径 。 然而复杂度炸上了天,那就要另辟蹊径。 然而复杂度炸上了天,那就要另辟蹊径。
一 、 合 并 两 个 数 组 D P 以 降 低 复 杂 度 \color{Red}{一、合并两个数组DP以降低复杂度} 一、合并两个数组DP以降低复杂度
上 面 D P 的 慢 , 是 因 为 每 次 都 要 枚 举 a 和 b 数 组 最 后 一 个 数 上面DP的慢,是因为每次都要枚举a和b数组最后一个数 上面DP的慢,是因为每次都要枚举a和b数组最后一个数
但 是 b 数 组 逆 序 接 在 a 数 组 , 可 以 发 现 就 是 一 个 不 降 序 数 组 , 就 是 求 长 度 2 ∗ m 的 不 降 序 数 组 个 数 。 但是b数组逆序接在a数组,可以发现就是一个不降序数组,就是求长度2*m的不降序数组个数。 但是b数组逆序接在a数组,可以发现就是一个不降序数组,就是求长度2∗m的不降序数组个数。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll n,m,ans;
ll dp[21][1001];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) dp[1][i]=1;
for(int i=2;i<=2*m;i++)
for(int j=1;j<=n;j++)
{
for(int q=1;q<=j;q++)
dp[i][j]=(dp[i][j]+dp[i-1][q])%mod;
if(i==2*m) ans=(ans+dp[i][j])%mod;
}
cout<<ans;
}
Ⅱ . 还 有 组 合 数 学 的 解 法 。 [ 当 然 不 是 我 想 的 ┭ ┮ ﹏ ┭ ┮ ] \color{Purple}{Ⅱ.还有组合数学的解法。[当然不是我想的┭┮﹏┭┮]} Ⅱ.还有组合数学的解法。[当然不是我想的┭┮﹏┭┮]
仍 然 要 注 意 到 b 的 最 小 元 素 ( 尾 元 素 ) 不 小 于 a 的 最 大 元 素 ( 尾 元 素 ) 仍然要注意到b的最小元素(尾元素)不小于a的最大元素(尾元素) 仍然要注意到b的最小元素(尾元素)不小于a的最大元素(尾元素)
因 为 a 不 下 降 , b 不 上 升 , 那 么 给 定 2 m 个 数 , 有 且 仅 有 1 种 方 案 组 成 符 合 条 件 的 a , b 数 组 因为a不下降,b不上升,那么给定2m个数,有且仅有1种方案组成符合条件的a,b数组 因为a不下降,b不上升,那么给定2m个数,有且仅有1种方案组成符合条件的a,b数组
也 就 是 说 , 从 1 − n 选 2 m 个 数 , 可 以 选 重 复 的 , 问 有 多 少 种 选 法 ? ? 也就是说,从1-n选2m个数,可以选重复的,问有多少种选法?? 也就是说,从1−n选2m个数,可以选重复的,问有多少种选法??
也 就 是 说 , 把 2 m 个 小 球 投 到 1 − n 个 盒 子 , 盒 子 可 以 为 空 , 有 多 少 种 投 法 。 也就是说,把2m个小球投到1-n个盒子,盒子可以为空,有多少种投法。 也就是说,把2m个小球投到1−n个盒子,盒子可以为空,有多少种投法。
为 了 方 便 , 先 把 n 个 盒 子 都 放 一 个 苹 果 , 也 就 是 2 ∗ m + n 放 在 n 个 盒 子 , 每 个 盒 子 至 少 放 一 个 为了方便,先把n个盒子都放一个苹果,也就是2*m+n放在n个盒子,每个盒子至少放一个 为了方便,先把n个盒子都放一个苹果,也就是2∗m+n放在n个盒子,每个盒子至少放一个
这 样 就 可 以 用 隔 板 法 。 2 ∗ m + n − 1 个 间 隙 , 从 中 选 出 n − 1 个 间 隙 放 隔 板 , 就 分 成 了 n 份 。 这样就可以用隔板法。2*m+n-1个间隙,从中选出n-1个间隙放隔板,就分成了n份。 这样就可以用隔板法。2∗m+n−1个间隙,从中选出n−1个间隙放隔板,就分成了n份。
答 案 是 C 2 ∗ m + n − 1 n − 1 答案是C_{2*m+n-1}^{n-1} 答案是C2∗m+n−1n−1
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll n,m,ans;
ll fac[2001];
ll qpow(ll a,ll n){
ll ans=1;
while(n){
if(n&1) ans=ans*a%mod;
a=a*a%mod;
n>>=1;
}
return ans;
}
ll C(ll n,ll m)
{
if(m>n) return 0;
return fac[n]*qpow(fac[m],mod-2)%mod*qpow(fac[n-m],mod-2)%mod;
}
ll Lucas(ll n,ll m)
{
if(!m) return 1;
return C(n%mod,m%mod)*Lucas(n/mod,m/mod)%mod;
}
int main()
{
cin>>n>>m;
fac[0]=1;
for(ll i=1;i<=2000;i++) fac[i]=(fac[i-1]*i)%mod;
cout<<Lucas(2*m+n-1,n-1);
}