洛谷 SP19148 / SPOJ INS14G Kill them All
比较经典的一道题。
思路
第一个怪物一定是 Digo 杀的,考虑第二个到最后一个怪物,如果忽略掉 Digo 杀的第一只怪物,那么每杀掉一只怪物后,Digo 的击杀数都不少于 Sharry 的击杀数。
假设现在在一个平面直角坐标系,位于点 \((0,0)\),Digo 杀一个怪物则向右上走一步,即 \((x,y) \to (x+1,y+1)\);Sharry 杀一个怪物则向右下走一步,即 \((x,y) \to (x+1,y-1)\)。则题要求的就是从 \((0,0)\) 走 \(n - 1\) 步且任意时刻纵坐标 \(\ge 0\),即不越过或在直线 \(y = -1\) 上的方案数。
考虑枚举终点坐标 \((n-1,k)\),其中 \(0 \le k \le n-1\) 且 \(2 \mid (n+k-1)\)。如果不考虑纵坐标 \(\ge 0\) 的限制,则从 \((0,0)\) 走到 \((n-1,k)\) 的方案数为 \(C_{n-1}^{\frac{n+k-1}{2}}\)。现在要减去不合法情况的方案数,设路径第一次与 \(y = -1\) 交于点 \((m,-1)\),将路径横坐标 \(\ge m\) 的部分沿 \(y = -1\) 翻折,则终点变为 \((n-1,-k-2)\)。因为从 \((0,0)\) 到 \((n-1,-k-2)\) 一定要经过 \(y = -1\),所以不合法情况的方案数其实就是 \((0,0)\) 到 \((n-1,-k-2)\) 的方案数,即 \(C_{n-1}^{\frac{n+k+1}{2}}\)。
那么答案为
容易发现式子的项可以相互抵消,因此当 \(2 \mid n\) 时答案为 \(C_{n-1}^{\frac{n}{2}}\),\(2 \nmid n\) 时答案为 \(C_{n-1}^{\frac{n-1}{2}}\)。
代码
code
/*
p_b_p_b txdy
AThousandSuns txdy
Wu_Ren txdy
Appleblue17 txdy
*/
#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 1000100;
const int N = 1000000;
const ll mod = 1000000007;
ll fac[maxn], ifac[maxn], n;
ll qpow(ll b, ll p) {
ll res = 1;
while (p) {
if (p & 1) {
res = res * b % mod;
}
b = b * b % mod;
p >>= 1;
}
return res;
}
void init() {
fac[0] = 1;
for (int i = 1; i <= N; ++i) {
fac[i] = fac[i - 1] * i % mod;
}
ifac[N] = qpow(fac[N], mod - 2);
for (int i = N - 1; ~i; --i) {
ifac[i] = ifac[i + 1] * (i + 1) % mod;
}
}
ll C(ll n, ll m) {
if (n < m || n < 0 || m < 0) {
return 0;
} else {
return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
}
void solve() {
scanf("%lld", &n);
if (n & 1) {
printf("%lld\n", C(n - 1, (n - 1) / 2));
} else {
printf("%lld\n", C(n - 1, n / 2));
}
}
int main() {
init();
int T = 1;
scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}