T4解法

概念:

  1. 可见山,不可见山:字面意思。
  2. 块:两个可见山之间的部分。参考下图:

  1. invxx 的逆元。

前置芝士:

  1. 逆元

只需知道 abmodP=(amodP)×(invbmodP)modP 即可。

  1. 求积公式

为了方便表达,i=1nai 表示 a1×a2××an (类比 理解)。

思路:

根据题意可知,可见山是最高的 l+r1 座山。

去掉其中最高的山,还剩 l+r2 座山。

我们只需在这 l+r2 座山中:

  1. 选取 l1 座,升序排在最高的山左边
  2. 剩下 r1 座,降序排在最高的山右边
  3. 还剩 nlr+2 座不可见山,插在可见山形成的块里

就可以满足题意。

我们分别考虑前两步和第三步对总方案数的影响:

前两步:从 l+r2 中选 l1 个,

方案数很明显是:Cl+r2l1modP=(l+r2)!(l1)![(l+r2)(l1)]!modP=(l+r2)!(l1)!(r1)!modP=(l+r2)!modP×inv(l1)!modP×inv(r1)!modP

( mod 优先级视为与乘法相同)

当然,我们不太可能把逆元算到 100! 的数量级,所以:(l+r2)!(l1)!(r1)!modP=(l+r2)!÷1÷2÷(l1)÷1÷2÷(r1)modP=(l+r2)!×i=1l11i×i=1r11imodP=(l+r2)!modP×i=1l1invimodP×i=1r1invimodP 别看上面的公式长得吓人,特别好理解

所以我们可以提前预处理出 1min(l1,r1) 的逆元:

for(int i = 2;i <= max(l, r) - 1;++i)
    inv[i] = (P - (P / i)) * inv[P % i] % P;

然后令 invx= 原来的 i=1xinvi,也就是把 inv 变成一个“前缀积”数组:

for(int i = 2;i <= max(l, r) - 1;++i)
    inv[i] = inv[i - 1] * inv[i] % P;

这样,原式就变为了:

(l+r2)!modP×invl1modP×invr1modP,简单了很多。

当然这个“前缀积”优化可加可不加。

第三步:不可见山的方案

我们设 f[i][j] 表示有 i 座山,j 个块时的不可见山插入方案数。

根据题意可知,有 l+r2 个块,那么我们只需要递推得到 f[n][l+r2] 即可。

现在我们来推式子:


首先,i 座山一定是由 i1 座山 +1 座山得来的,

f[i][j] 是由 f[i1][] 转移而来的。

我们需要讨论 + 的那一座山插在哪:

(下面的图中,黑色代表转移前的 i1 座山,红色代表新来的一座山)

(假设山是从小到大依次插入的,顺序不会影响总方案数)


第一种情况:插在两边

由图可知,原有 j1 个块,i1 个点,即转移前状态为 f[i1][j1]

(插到最左边同理)

另外需要注意,转移时不需要分类讨论插到最左边和最右边的情况。

因为两边的差别实际上是可见山的差别,对于不可见山这两种方案是一样的。

而且,f[i][j] 表示的是不可见山的方案数。

所以 f[i][j] 是由 f[i1][j1]一种状态转移而来的,并非左右两种。

第二种情况:插在中间

由图可知,插入前有 j 个块,转移前状态 f[i1][j]

但是这个新来的山可以插在中间的任何位置。

原有 i1 座山,有 i2 个空(并非块)可以插入。

所以这个状态转移时要 ×(i2)

我们就得到方程:

f[i][j]=(f[i1][j]×(i2)+f[i1][j1])modP

容易想到,每种可见山的方案都有多种不可见山插入方案,

所以答案应为可见山方案×不可见山方案,

f[n][l+r2]×(l+r2)!modP×invl1modP×invr1modP

终于没了。思路这么清晰还要什么代码?

#include <iostream>
#define int long long
#define P 1000000007
using namespace std;
int fac[150], inv[150], f[50050][250], n, l, r, ans;
signed main()
{
    inv[0] = inv[1] = fac[1] = 1;f[1][0] = 1;
    cin >> n >> l >> r;
    for(int i = 2;i <= max(l, r) - 1;++i)
        inv[i] = (P - (P / i)) * inv[P % i] % P;
    for(int i = 2;i <= max(l, r) - 1;++i)
        inv[i] = inv[i - 1] * inv[i] % P;
    for(int i = 2;i <= l + r - 2;++i)
        fac[i] = fac[i - 1] * i % P;
    for(int i = 1;i <= n;++i)
        for(int j = 1;j <= l + r - 2;++j)
            f[i][j] = (f[i - 1][j] * (i - 2) + f[i - 1][j - 1]) % P;
    cout << f[n][l + r - 2] * fac[l + r - 2] % P * inv[l - 1] % P * inv[r - 1] % P;
    return 0;
}
posted @   Jijidawang  阅读(1)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示