P9018 Moo Route G
【大体思路】
数形结合+组合数。
【详解】
Part 1. 发散思路
受 P1641 的启发,这道题在一开始模拟样例的时候就尝试用数形结合的方法来体现过程。将原本在数轴上的路径抻开,变成折线放到平面直角坐标系中,就会直观多了。以及,
比如这张图,一行一行横向来看,从下往上数第一层之间的红色线段的数量
因为最终又回到了
题目对于路径有一个限制,那就是要求转头次数最少,对应到图上就是折角要尽可能少,照着样例再画一画就能发现,存在比上图转头
这种方案共转头了
第一张图下面那一层的两个尖中,左边那个尖中间是空的,右边那个尖里一共插入了
第二张图下面那一层的两个尖中,左边那个尖中间插入了
这是上层尖比下层尖多的情况,如果下面的尖比较多,那么一定有下面的尖是空的,这时候就要在底下一层选出跟上面一层尖的数量相同的尖,一一对应地放,这样就尽可能使转头次数少了。
说到这,感觉很容易就能把这个问题跟另一个问题联系起来了:这不就是在
不过先别急,这只是
接着苹果篮子的问题来说,我们可以把
回到原题,对于任意一层的尖,放置的时候只跟它下面的那层有关系,其它层对它产生不了影响。这样也就把
OK,思路就到这,接着是一些方案数计算方法,已经明白的可以跳过了!
Part 2. 计算方法
设上层有
按照上面的情况进行判断,然后将结果累乘起来就可以了。
【代码】
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int p = 1e9 + 7;
int n;
ll ans, jc[N], a[N];
ll ksm(ll a, ll b) {
ll ret = 1;
while (b) {
if (b & 1) {
ret = (ret % p * a % p) % p;
}
a = (a * a) % p;
b >>= 1;
}
return ret % p;
}
ll C(int a, int b) {
return jc[a] * ksm((jc[a - b] % p * jc[b] % p) % p, p - 2) % p;
}
int main() {
scanf("%d", &n);
ans = 1;
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
a[i] /= 2;
}
jc[1] = 1;
for (int i = 2; i <= 1000000; i++) {
jc[i] = (jc[i - 1] % p * i % p) % p;
}
for (int i = 1; i < n; i++) {
if (a[i + 1] == a[i] || a[i] == 1) {
continue;
}
if (a[i + 1] == 1) {
ans = (ans % p * a[i] % p) % p;
continue;
}
if (a[i + 1] >= a[i]) {
ans = (ans % p * C(a[i + 1] - 1, a[i] - 1) % p) % p;
} else {
ans = (ans % p * C(a[i], a[i + 1]) % p) % p;
}
}
printf("%lld", ans % p);
return 0;
}