LOJ6614 THUPC 2019 过河卒二(chess)
题目链接
题目大意
给你一个 $n$ 行 $m$ 列的棋盘,从 $(1, 1)$ 走到 $(n, m)$ ,有 $k$ 个点不能经过,每次可以向上向右或者向斜上方走。问有多少走法可以走出棋盘。结果对 $59393$ 取模。
题目分析
由于所有的路径均需走出棋盘,那么显然只会走到 $n + 1$ 行或者 $m + 1$ 列,那么显然如果不允许他们继续走向其他的行或列,那么显然走出去后都有唯一的路径走到 $(n + 1, m + 1)$ 点。也就是说,题目便转化为从 $(1, 1)$ 走到 $(n + 1, m + 1)$ 。
此时再考虑从$(0, 0)$ 走到 $(x, y)$ ,显然需要走 $x$ 步向上, $y$ 步向右,由于还有可能斜着走,注意到 $m$ 是 $10 ^ 5$ 量级的。因此可以枚举斜着走的次数,那么可以获得如下表达式:
$$\sum\limits_{i=0}^{\min(x, y)}{C_{x + y – 2i}^{x – i} \cdot C_{x + y – i}^{i}}$$
注意到 $n$ 是 $10 ^ 9$ 量级的,而模数是 $59393$,因此需要使用 $Lucas$ 定理来求组合数。
之后考虑不经过 $k$ 个点,可以使用容斥原理解决。
不过 $Lucida$ 跟我讲了另一种做法,统计第一个经过的点是谁的方法数即可,这样显然也是补充不漏的,求解速度还会更快一点。
代码
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <string> #include <algorithm> using namespace std; const int maxn = 1e5 + 5; long long mod = 59393; int n, m, k, t; struct point { long long x, y; } p[25]; long long fac[maxn]; long long inv[maxn]; long long f[25][25]; long long g[25][2]; long long h[25]; int read(){ int x; scanf("%d", &x); return x; } bool cmp(point x, point y){ if(x.x == y.x)return x.y < y.y; return x.x < y.x; } long long qpow(long long b, long long x){ long long ret = 1; while(x){ if(x & 1){ ret = ret * b % mod; } b = b * b % mod; x >>= 1; } return ret; } long long getc(long long x, long long y){ if(x < y)return 0; return fac[x] * inv[x - y] % mod * inv[y] % mod; } long long lucas(long long x, long long y){ if(y == 0)return 1; return getc(x % mod, y % mod) * lucas(x / mod, y / mod) % mod; } int init(){ fac[0] = 1; inv[0] = 1; for(long long i=1;i<mod;i++){ fac[i] = fac[i - 1] * i % mod; inv[i] = qpow(fac[i], mod - 2); } return 0; } int main(){ int i, j; long long l, r; long long x, y; long long ans = 0; init(); n = read(); m = read(); k = read(); for(i=0;i<k;i++){ p[i].x = read(); p[i].y = read(); } sort(p, p + k, cmp); for(i=0;i<k;i++){ for(j=i+1;j<k;j++){ if(p[i].y > p[j].y)continue; l = p[j].x - p[i].x; r = p[j].y - p[i].y; for(x=0;x<=min(l,r);x++){ f[i][j] = (f[i][j] + lucas(l + r - 2 * x, l - x) * lucas(l + r - x, x) % mod) % mod; } } } for(i=0;i<k;i++){ l = p[i].x - 1; r = p[i].y - 1; for(x=0;x<=min(l,r);x++){ g[i][0] = (g[i][0] + lucas(l + r - 2 * x, l - x) * lucas(l + r - x, x) % mod) % mod; } l = n - p[i].x + 1; r = m - p[i].y + 1; for(x=0;x<=min(l,r);x++){ g[i][1] = (g[i][1] + lucas(l + r - 2 * x, l - x) * lucas(l + r - x, x) % mod) % mod; } } l = n; r = m; for(x=0;x<=min(l,r);x++){ ans = (ans + lucas(l + r - 2 * x, l - x) * lucas(l + r - x, x) % mod) % mod; } /*第二种做法*/ for(i=0;i<k;i++){ h[i] = g[i][0]; for(j=0;j<k;j++){ h[i] -= h[j] * f[j][i] % mod; h[i] %= mod; } ans -= h[i] * g[i][1] % mod; ans %= mod; } /*容斥做法*/ /*for(i=1;i<(1<<k);i++){ x = -1; long long tmp = 1; int cnt = 0; for(j=0;j<k;j++){ if((i >> j) & 1){ cnt++; if(x == -1){ tmp = (tmp * g[j][0]) % mod; }else{ tmp = (tmp * f[x][j]) % mod; } x = j; } } tmp = (tmp * g[x][1]) % mod; ans += (cnt % 2 == 0 ? 1ll : -1ll) * tmp; ans %= mod; }*/ ans = (ans % mod + mod) % mod; printf("%lld\n", ans); return 0; }