题目描述
给你大小为的棋盘那就有
个格点,你要从左下点走到右上点问方案数,每次只能往上或者往右走。而且这个棋盘里面存在
个格点无法走入。
Solution
首先看,当,是可以通过二维的
求解过程,这个过河卒问题很基础我就不多解释了,遇到障碍跳过即可。
重点看后面很大,但是会发现他们的
很小很小,最多只有 3 个障碍,那么我们知道,如果不存在障碍的情况下,假设要往上走
步,往下走
步,过河卒从左下往右上走的方案数是
。怎么来的,就是一共需要走
步可以到终点,这些步骤里面,要选出
步进行上移操作,那么剩下
步里面只能选择右移了。那么知道怎么计算从左下点到右上点的方案数之后,我们就可以找到新的方案去转移了。
我们假设是从
走到第
个障碍的合法方案数,这里我们首先需要按
更小的尽可能放在前面,
更小的也要尽可能放在更大的前面,当然
的相对关系没什么必要比较,只需要保证
和
有一个比之前的点大,我就应该把这个点放在刚刚找到的之前的点后面,这也是我们减掉不合法路径的来源。我们知道如果一个障碍点前面没有新的障碍点,那么它的方案数很容易计算,就是把他看成右上点计算一下即可,但是如果一个点,他曾经走过的路径上会有障碍点,这样的路径是不能计算的,也就是
同时都小于当前障碍点的前驱障碍点。通过这个障碍点产生的路径数是不合法的需要减掉。那么假设前驱障碍点是
,现在遍历到的是
,那么之前我们一定计算出了
的值,就是从起点到第
个障碍点的方案数,那么一定要经过这个点去到第
个障碍点的方案数就是,
乘上以第
个障碍点为起点,第
个障碍点为终点的过河卒方案数。依次减掉即可,为了求解方便我们可以假设目标位置是一个障碍点,这样转移到最后一个障碍点的时候就会求到目标位置的合法方案数。
)比较需要注意的是,这个题目的坐标是一个左上点。。需要特别小心一点。
#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define all(__vv__) (__vv__).begin(), (__vv__).end()
#define endl "\n"
#define pai pair<int, int>
#define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__))
#define rep(i, sta, en) for(int i=sta; i<=en; ++i)
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar()) s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; }
inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op) putchar(op); return; } char F[40]; ll tmp = x > 0 ? x : -x; if (x < 0)putchar('-'); int cnt = 0; while (tmp > 0) { F[cnt++] = tmp % 10 + '0'; tmp /= 10; } while (cnt > 0)putchar(F[--cnt]); if (op) putchar(op); }
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans *= a; b >>= 1; a *= a; } return ans; } ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }
const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
const int MOD = 998244353;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 7;
const int M = 1e3 + 7;
int n, m, k;
ll dp[M][M], jc[N << 1], inv[N << 1];
bool mp[M][M];
struct Node {
int x, y;
bool operator < (const Node& opt) const {
return x < opt.x or y < opt.y;
}
}a[N];
void init() { // 线性逆元
jc[0] = 1;
rep(i, 1, 2 * N - 1)
jc[i] = jc[i - 1] * i % MOD;
inv[2 * N - 1] = qpow(jc[2 * N - 1], MOD - 2, MOD);
for (int i = 2 * N - 2; ~i; --i)
inv[i] = inv[i + 1] * (i + 1) % MOD;
}
void DP() {
dp[0][1] = 1;
rep(i, 1, k) mp[a[i].x][a[i].y] = 1;
rep(i, 1, n) {
rep(j, 1, m) {
if (mp[i][j]) continue;
dp[i][j] = (dp[i][j - 1] + dp[i - 1][j]) % MOD;
}
}
print(dp[n][m]);
}
ll C(int n, int m) {
return jc[n] * inv[n - m] % MOD * inv[m] % MOD;
}
ll calc(int x, int y) {
return C(x + y, x);
}
void solve() {
n = read() + 1, m = read() + 1, k = read();
rep(i, 1, k)
a[i].x = n - read() + 1, a[i].y = read();
if (n < M and m < M) { DP(); return; }
init();
a[++k] = { n,m };
sort(a + 1, a + 1 + k);
ll ans[5];
rep(i, 1, k) {
ans[i] = calc(a[i].x - 1, a[i].y - 1);
rep(j, 1, i - 1) {
if (a[j].x < a[i].x and a[j].y < a[i].y)
ans[i] -= ans[j] * calc(a[i].x - a[j].x, a[i].y - a[j].y) % MOD,
ans[i] = (ans[i] + MOD) % MOD;
}
}
print(ans[k]);
}
int main() {
//int T = read(); while (T--)
solve();
return 0;
} 
京公网安备 11010502036488号