题解 LGP9018【[USACO23JAN] Moo Route G】

problem

现在有一条数轴,\(t\) 表示当前时刻。在 \(t=0\) 时 Bessie 恰好处在 \(x=0\) 的位置。

接下来,每秒钟 Bessie 会向左或者向右移动一个单位距离,我们保证 Bessie 是在 \(0-N\) 的位置之间移动并最终停在 \(x=0\) 的位置。同时,我们有一个 \(A_0,A_1,A_2\ldots A_{N-1}\) 的数列,分别表示 Bessie 经过 \(0.5,1.5,2.5\ldots (N-1).5\) 这些点的次数。我们可以用一个由 \(\text{L}\)\(\text{R}\) 组成的序列来表示 Bessie 的路径,我们称 Bessie 改变了一次方向为在序列中的相邻两个字符不同。现在我们不知道具体的移动序列是什么,但我们知道 Bessie 采用了让她改变方向次数最少的走法。现在请问 Bessie 的路径有多少种不同的可能情况?(我们称两条路径不同当且仅当这条路径对应序列中的某一位不同)

\(10^9+7\) 取模,\(N\le10^5,\max(A_i)\le10^6\)

明显 \(a_i\) 全为偶数,所以我们将其除以二。

solution

假如求出前 \((i-1)\) 个的答案,现在有 \(a_{i-1}\) 个回路的一端在 \((i-1)\) 处,考虑接到 \(a_i\) 上。

因为 Bessie 采用了让她改变方向次数最少的走法,我们要尽量的让 Bessie 转弯次数尽可能的少。
一个显然的贪心是将尽量多的 \(a_i\) 接到 \(a_{i-1}\) 上,使每一个 \(a_{i-1}\) 的回路都接到 \(i\) 处。

如果 \(a_{i-1}\geq a_i\),发现接不完,但还是要接,给每一个 \(a_i\) 安排一个 \((i-1)\) 的回路。
因为回路之间是没有区别的,所以方案数为:\(\binom{a_{i-1}}{a_i}\)

如果 \(a_{i-1}<a_i\),发现接完了还有剩的。考虑为了贪心,我们先给每一个 \((i-1)\) 的回路安排一个。
此时剩下 \(a_i-a_{i-1}\) 个回路,随便放在 \((i-1)\),发现是一个经典插板法。
仔细考虑发现不需要插板:考虑第一个 \((i-1)\) 的回路一定是要了 \((i)\) 的一个回路;如果确定了其余每一个 \((i-1)\) 回路接上哪一个回路,那么我们断言:两个匹配的 \((i)\) 之间直接放进去就可以了。
同样可以推出结论为:\(\binom{a_i-1}{a_{i-1}-1}\)

code

点击查看代码
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
const int P=1e9+7;
LL mod(LL x){return (x%P+P)%P;}
void red(LL&x){x=mod(x);}
LL qpow(LL a,LL b,int p){LL r=1;for(a%=p;b;b>>=1,a=a*a%p) if(b&1) r=r*a%p; return r;}
template<int N,int P> struct C_prime{
	LL fac[N+10],ifac[N+10];
	C_prime(){
		for(int i=fac[0]=ifac[0]=1;i<=N;i++) fac[i]=fac[i-1]*i%P;
		ifac[N]=qpow(fac[N],P-2,P);
		for(int i=N-1;i>=1;i--) ifac[i]=ifac[i+1]*(i+1)%P;
	}
	LL operator()(int n,int m){return fac[n]*ifac[n-m]%P*ifac[m]%P;}
};
int n,a[100010];
LL res=1;
C_prime<1000010,P> C;
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]>>=1;
	for(int i=2;i<=n;i++){
		if(a[i-1]>=a[i]) red(res*=C(a[i-1],a[i]));
		else red(res*=C(a[i]-1,a[i-1]-1));
	}
	printf("%lld\n",res);
	return 0;
}

错解

不要模仿
## solution -1

以下设 $f(n,m)=\binom{n+m-1}{n-1}$ 表示 $n$ 个相同的球放入 $m$ 个互不相同的盒子中的方案数,是隔板法。

$n\leq 10^5,1\leq a_i\leq 10^6$ 且 $a_i$ 为偶数,于是下文的讨论中我们直接除以二。

题目说是最小化拐弯次数,不如这么想,你有很多个区间恰好覆盖某些点 $a_i$ 次,每加入一个区间你的拐弯次数都得加二,所以一种最优的方案是这样的:
- 对着值域作扫描线,对于所有 $a_i\geq v$ 形成的连续段,加入这些区间。
- (所以积木大赛的贪心策略是正确的)

## solution 0:$n=2$
当 $a_1<a_2$ 时答案是 $f(a_2-a_1,a_1)$,否则 $f(a_1-a_2,a_2+1)$。

我们要看到的是,假如一堆很长的区间有 $x$ 个,加入一个新的包含在它们之中的区间时,
- 如果上一个区间和新区间完全相同我们要特判,这里不能考虑。
- 如果挨着左边,则插入方案数为 $x+1$。
- 如果挨着右边,则插入方案数为 $x$。
- 否则,$2x$。

## solution 1:差分绝对值等于一
考虑对着值域扫描线,然后插入那一些在值域上的连续段。

然后它底下根据 sol0 算方案数。

## solution 2
对着值域扫描线,对于完全相同的区间合并计算,插板,然后其它的照常维护。。。。。。

用珂朵莉树那一套应该好写一点的说。。。。
posted @ 2023-02-11 23:23  caijianhong  阅读(33)  评论(0编辑  收藏  举报