P9018 [USACO23JAN] Moo Route G 题解

首先有一些性质。因为保证有解,所以 \(a_i\) 一定都是 \(2\) 的倍数(必须一来一回)。并且总的步数应该为 \(\sum a_i\)

先考虑 \(n \le 2\) 的情况,这时我们可以分情况讨论。因为每一条线段都会被来回走两次,所以我们可以先把每一个数都除以 \(2\)

  1. \(a =b\),则最优情况一定是形如 \(RRLLRRLL\dots\) 的,否则转向次数一定更多。

  2. \(a<b\),我们假设先按上述情况的方式走完,那么右边线段应该还需要再被走 \(b-a\) 次。考虑这 \(b-a\) 次在什么时候走最优,会发现如果选择走到中间点的时候再走一次 \(RL\) 是最优的。所以我们每一次走到中间点都可以选择走一次 \(RL\)。最先总共有 \(a\) 个中间点,每走一次 \(RL\) 还会多产生一个中间点,所以总方案数是 \(\frac{\prod_{i=a}^{b-1}i}{(b-a)!}\)

  3. \(a > b\),那么左边线段应该还需要再被走 \(a-b\) 次。则会发现如果走到最左边的时候再多走一次 \(RL\) 是最优的。最先总共有 \((b+1)\) 个左边点,每一次走 \(RL\) 还会多产生一个,所以总方案数是 \(\frac{\prod_{i=(b+1)}^{a}i}{(a-b)!}\)

现在考虑把 \(n\) 扩展到 \(10^5\)。这时我们会发现,如果我们从左往右考虑的话,那么 \(a_{i+1}\) 的选择只和 \(a_i\) 有关系,和 \(a_{i-1}\) 没有关系。所以我们可以从左往右依次将序列拆分为 \([a_1,a_2],[a_2,a_3]\) 这样长度为 \(2\) 的段,对于每一段都可以按照 \(n=2\) 的情况单独算,最终答案即为它们相乘。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7; 
const int MAXN = 2e6 + 10;
int n,a,b,fac[MAXN],inv[MAXN],invfac[MAXN],p[MAXN],ans = 1;
inline int P(int l,int r){return (fac[r] * invfac[l - 1]) % mod;}
signed main()
{
	inv[0] = fac[0] = invfac[0] = 1;
	inv[1] = fac[1] = invfac[1] = 1;
	for(int i = 2;i <= 2000000;i++)
	{
		inv[i] = (inv[mod % i] * (mod - mod / i)) % mod;
		fac[i] = (fac[i - 1] * i) % mod;
		invfac[i] = (invfac[i - 1] * inv[i]) % mod;
	} 
	scanf("%lld",&n);
	for(int i = 1;i <= n;i++) scanf("%lld",&p[i]);
	for(int i = 2;i <= n;i++)
	{
		a = p[i - 1],b = p[i];
		int tmp;
		if(a % 2 == 1 || b % 2 == 1) tmp = 0;
		else if(a == b) tmp = 1;
		else if(b > a) tmp = (P(a / 2,a / 2 + (b - a) / 2 - 1) * invfac[(b - a) / 2]) % mod;
		else if(a > b) tmp = (P(b / 2 + 1,b / 2 + (a - b) / 2) * invfac[(a - b) / 2]) % mod;
		ans = (ans * tmp) % mod;
	} 
	printf("%lld",ans);
	return 0;
}

posted @ 2024-01-17 09:18  Creeper_l  阅读(12)  评论(0编辑  收藏  举报