Link
http://codeforces.com/contest/559/problem/C
Description
n×m 的网格,要从 (1,1) 走到 (n,m) ,只能沿着行或者列增加的方向走,又其中有 k 个地方不能走,问路径数目。
Data scope
1≤n≤105
1≤m≤105
1≤k≤2000
Solution
X S X X T X
显然是容斥原理问题,为此我们把禁止走的格子标号。并且设 f(i1i2...ip) 表示走 i1,i2,...,ip 这些禁止格子的路径数。
1 S 2 3 T 4
那么:
result=f(null)−f(1)−f(2)−f(3)−f(4)+f(12)+f(13)+f(14)+f(23)+f(24)+f(34)−f(123)−f(124)−f(134)−f(234)+f(1234)
f(i1i2...ip) 的求法并不复杂,它是一系列组合数的乘积,这里不赘述。
距离解决问题的障碍是项太多。一共有 2k 项,但是应该认识到是有些项的路径是不存在的。
比如 f(14) 就是不可能的,但是 f(23) 就是可以的。原因嘛……
这就启发我们对每一个点计算一个值 dp[u] ,表示经过这个点之后要乘的组合数的和。
dp[u]=calc(u,T)−∑vx≥ux,vy≥uycalc(u,v)dp[v]
result=calc(S,T)−∑calc(S,u)dp[u]
Evaluation
Not bad…
Code
#include <bits/stdc++.h>
const int MAXK = 2000 + 5;
const int MOD = int(1e9) + 7;
std::pair<int, int> ban[MAXK];
#define fst first
#define sec second
std::vector<int> g[MAXK];
long long dp[MAXK];
const int MAXN = int(2e5) + 5;
long long fac[MAXN];
long long inv_fac[MAXN];
int n, m, k;
void fac_table() {
fac[0] = 1;
for (int i = 1; i < MAXN; i++)
fac[i] = fac[i - 1] * i % MOD;
inv_fac[0] = 1;
inv_fac[1] = 1;
for (int i = 2; i < MAXN; i++)
inv_fac[i] = (MOD - MOD / i) * inv_fac[MOD % i] % MOD;
for (int i = 2; i < MAXN; i++)
inv_fac[i] = inv_fac[i] * inv_fac[i - 1] % MOD;
for (int i = 0; i < MAXN; i++)
assert(fac[i] * inv_fac[i] % MOD == 1);
}
long long pascal(int n, int m) {
return fac[n] * (inv_fac[m] * inv_fac[n - m] % MOD) % MOD;
}
long long calc(int x, int y, int p, int q) {
return pascal(p + q - x - y, p - x);
}
long long calc(int u) {
return calc(ban[u].fst, ban[u].sec, n, m);
}
long long calc(int u, int v) {
return calc(ban[u].fst, ban[u].sec, ban[v].fst, ban[v].sec);
}
int dfs(int u) {
if (dp[u] != -1)
return dp[u];
dp[u] = calc(u);
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
dp[u] = (dp[u] + calc(u, v) * (MOD - dfs(v)) % MOD) % MOD;
}
return dp[u];
}
int main() {
fac_table();
scanf("%d %d %d", &n, &m, &k);
for (int i = 0; i < k; i++) {
scanf("%d %d", &ban[i].fst, &ban[i].sec);
}
for (int i = 0; i < k; i++) {
for (int j = 0; j < k; j++) {
if (i == j)
continue;
if (ban[j].fst >= ban[i].fst && ban[j].sec >= ban[i].sec)
g[i].push_back(j);
}
}
memset(dp, -1, sizeof(dp));
long long res = calc(1, 1, n, m);
for (int i = 0; i < k; i++) {
res = (res + calc(1, 1, ban[i].fst, ban[i].sec) * (MOD - dfs(i))) % MOD;
}
printf("%d\n", (int) res);
return 0;
}