LGV 引理
QOJ2209
题目描述
You want to walk from \((0,0, \ldots, 0)\) to \(\left(a_0, a_1, \ldots, a_{n-1}\right)\) in an \(n\)-dimensional space. On each step, you can increase one component of your coordinate vector by one. There are \(m\)obstacles \(p_1, p_2, \ldots, p_m\). You want to find the number of paths that don't pass through the obstacles.
However, this problem is too simple for an ICPC contest in the year 8102. We add one more constraint. For every point \(\left(x_0, x_1, \ldots, x_{n-1}\right)\) on your path, the components of this vector should be non-decreasing: \(x_0 \leq x_1 \leq \ldots \leq x_{n-1}\).
Output the number of paths modulo \(10^9+7\).
The first line contains two integers \(n\) and \(m(1 \leq n \leq 50,0 \leq m \leq 50)\).
The second line contains \(n\) integers \(a_0, a_1, \ldots, a_{n-1}\left(0 \leq a_0 \leq a_1 \leq \ldots \leq a_{n-1} \leq 10^4\right)\), the coordinate vector of your destination.
The following \(m\)lines describe obstacles. The \(i\)-th of these lines contains \(n\) integers \(p_{i, 0}, p_{i, 1}, \ldots, p_{i, n-1} \left(0 \leq p_{i, 0} \leq p_{i, 1} \leq \ldots \leq p_{i, n-1} \leq 10^4\right)\), the coordinate vector of an obstacle.
The starting point, destination, and all the obstacles are distinct.
Output the answer modulo \(10^9+7\).
题解
先不考虑无法经过的格点,我们先将 \(a_i\) 不严格递增转换一下。设 \(b_i = a_i + i\),问题转化成从 \((1, 2, \cdots, n)\) 到 \((b_1, b_2, \cdots, b_n)\) 且中途 \(x_i\) 保持严格递增的方案数。
引理 \(1\):不考虑 \(x_i\) 严格递增的限制,此时从 \((s_1, s_2, \cdots, s_n)\) 走到 \((c_1, c_2, \cdots, c_n)\) 的方案数是 \(\binom{\sum c_i - s_i}{c_1-s_1, c_2 - s_2, \cdots, c_n - s_n} = (\sum c_i - s_i)! \times \prod\dfrac{1}{(c_i - s_i)!}\) 。
下标严格递增可以表示为网格图的形式。我们将第 \(i\) 个维度视为一条从 \((0, i)\) 到 \((\sum (b_i - i), b_i)\) 的路,那么会有如下条件:
- 由于一个维度一次最多 \(+1\),如果我们将向右上视为 \(+1\),向右视为不变,那么这条路径只能向右 / 向右上走。
- 由于每次只能移动相邻一格,因此每一步只能有一条路径向右上,其他路径向右。
- 由于中途 \(x_i\) 要保持递增,因此每条路径都应该互不相交。
由于我们想消除第三个条件,因此引入 LGV 引理。
设 \(w(P)\) 为路径 \(P\) 上的边权之积,\(e(u, v)\) 表示 \(u\) 到 \(v\) 每一条路径 \(P\) 的 \(w(P)\) 之和,起点、终点的点集为 \(A, B\)。那么有:
\[M = \begin{bmatrix} e(A_1, B_1) & e(A_1, B_2) & \cdots & e(A_1, B_n)\\ e(A_2, B_1) & e(A_2, B_2) & \cdots & e(A_2, B_n)\\ \vdots & \vdots & \ddots & \vdots \\ e(A_n, B_1) & e(A_n, B_2) & \cdots & e(A_n, B_n)\\ \end{bmatrix}\\ \begin{aligned} \text{det}(M) = & \sum_{{\substack{S: A \to B \\ S \text{为不相交路径组}}}} \text{sgn}(\sigma(S)) \prod_{i=1}^{n} \omega(S_i)\\ \iff \sum_{\sigma} \text{sgn}(\sigma) \prod_{i = 1}^n e(A_i, B_{\sigma_i}) = & \sum_{{\substack{S: A \to B \\ S \text{为不相交路径组}}}} \text{sgn}(\sigma(S)) \prod_{i=1}^{n} \omega(S_i) \end{aligned} \]
引入一个类似的结论。
设 \(f(\sigma)\) 为 \(A_i \to B_{\sigma_i}\) 的所有路径组的权值之和。
\[\begin{aligned} \sum_{\sigma} \text{sgn}(\sigma) \prod_{i = 1}^n e(A_i, B_{\sigma_i}) = & \sum_{{\substack{S: A \to B \\ S \text{为不相交路径组}}}} \text{sgn}(\sigma(S)) \prod_{i=1}^{n} \omega(S_i)\\ \iff \sum_{\sigma} \text{sgn}(\sigma) f(\sigma) = & \sum_{{\substack{S: A \to B \\ S \text{为不相交路径组}}}} \text{sgn}(\sigma(S)) \prod_{i = 1}^n w(S_i)\\ \end{aligned} \]
回到原题。我们设推论中的 \(g(S)\) 表示路径组 \(S\) 是否满足前两个条件,\(f(\sigma)\) 就是所有 \(S: (0, i) \to (\sum(b_{\sigma_i} - \sigma_i), b_{\sigma_i})\) 的 \(g(S)\) 之和。由引理 \(1\),\(f(\sigma) = (\sum b_{\sigma_i} - i)! \times \prod\dfrac{1}{(b_{\sigma_i} - i)!}\)。
考虑一个路径组 \(S\)。由于这个路径起终点和网格图的特殊性,当 \(\sigma(S)\) 逆序对大于 \(0\) 时,\(S\) 一定会相交。因此,不存在不相交的 \(S\) 使得 \(\sigma(S)\) 逆序对大于 \(0\) 的。
设 \(A_i = (0, i), B_i = (\sum(b_{i} - i), b_{i}), T = \{A_i \to B_i\}\)。那么,就有:
将 \(M\) 的行列式求出即可。
#include <bits/stdc++.h>
using namespace std;
long long read() {
long long x = 0, k = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-')
k = -1, c = getchar();
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return x * k;
}
const int N = 107;
const int M = 1e6 + 7;
const int C = 1e6;
const long long mod = 1e9 + 7;
int n, m, a[N], s[N];
long long fac[M], ifac[M], px[N][N], dp[N];
struct Node {
int p[N];
bool operator<(const Node &x) const {
for (int i = 1; i <= n; i++) {
if (p[i] != x.p[i])
return p[i] < x.p[i];
}
return 1;
}
} tx[N];
long long ksm(long long x, long long y = mod - 2) {
long long res = 1;
for (; y; y >>= 1, (x *= x) %= mod)
if (y & 1)
(res *= x) %= mod;
return res;
}
long long solve(long long a[][N], int n) {
long long res = 1, w = 1;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++)
while (a[j][i]) {
int div = a[i][i] / a[j][i];
for (int k = i; k <= n; k++) a[i][k] = (a[i][k] - 1ll * div * a[j][k] % mod + mod) % mod;
swap(a[i], a[j]), w *= -1;
}
(res *= a[i][i]) %= mod;
}
return (res * w + mod) % mod;
}
long long f(Node p, Node q) {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += (q.p[i] - p.p[i]);
for (int j = 1; j <= n; j++) {
if (q.p[j] < p.p[i])
px[i][j] = 0;
else
px[i][j] = ifac[q.p[j] - p.p[i]];
}
}
if (sum < 0)
return 0;
return fac[sum] * solve(px, n) % mod;
}
void solve() {
n = read(), m = read();
for (int i = 1; i <= n; i++) a[i] = read() + i, s[i] = i, tx[0].p[i] = i, tx[m + 1].p[i] = a[i];
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++) tx[i].p[j] = read() + j;
sort(tx + 1, tx + m + 1);
fac[0] = 1;
for (int i = 1; i <= C; i++) fac[i] = fac[i - 1] * i % mod;
ifac[C] = ksm(fac[C]);
for (int i = C - 1; i >= 0; i--) ifac[i] = ifac[i + 1] * (i + 1) % mod;
dp[0] = 1;
for (int i = 1; i <= m; i++)
for (int j = 0; j < i; j++) (dp[i] += (mod - dp[j] * f(tx[j], tx[i]) % mod)) %= mod;
long long ans = 0;
for (int i = 0; i <= m; i++) (ans += dp[i] * f(tx[i], tx[m + 1]) % mod) %= mod;
cout << ans << '\n';
}
signed main() {
int t = 1;
while (t--) solve();
return 0;
}
/*
4 2
1 2 3 4
0 1 2 3
1 1 2 2
*/
QOJ2568
题目描述
Damir is climbing mountains. The mountain map can be represented as an \(n \times m\)grid, in which a cell at the intersection of row \(i\)and column \(j\)is denoted as \((i, j)\). The height of the peak in cell \((i, j)\)is equal to a non-negative integer \(a_{i, j}\). Damir starts his journey on the peak in cell \((1,1)\) aiming to reach the peak in cell ( \(n, m\)). If Damir is in on the peak in cell ( \(i, j\)), then he can go either to the peak in cell ( \(i+1, j\)) or to the peak in cell \((i, j+1)\). Of course, he cannot go outside the boundaries of the map. To make the journey more interesting, he chooses the path with the largest sum of peak heights (kind of total climb).
Damir loves combinatorics, and he became curious: how many \(n \times m\) maps are there such that the sum of peak heights on his path does not exceed \(k\)? As the answer may be large, find it modulo \(10^9+7\).
The only line of input contains three integers, \(n, m\), and \(k(1 \leq n, m, k \leq 100)\).
Print the answer modulo \(10^9+7\).
题解
将异色之间的分界线作为网格图上的路径。为使得路径之间是严格不交,将第 \(i\) 个初始点和第 \(i\) 个终点向上提高 \(k - i\) 格。容易发现只有 \(k\) 条分界线。
对于 \((0, x) \to (m, y)\) 的路径,方案数是 \(\binom{m}{y - x + m}\)。设 \(e(A_i, B_j) = \binom{m - j + i}{n + m - 2j + 2i}\),设 \(A = \{0 \leq i < k \mid (i, -i)\}, B = \{0 \leq i < k \mid (m + i, n - i)\}\),那么由 LGV 引理,答案就是:
#include <bits/stdc++.h>
using namespace std;
long long read() {
long long x = 0, k = 1; char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') k = -1, c = getchar();
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return x * k;
}
const int N = 107;
const int M = 507;
const long long mod = 1e9 + 7;
int n, m, k;
long long a[N][N], c[M][M];
long long solve(long long a[][N], int n) {
long long res = 1, w = 1;
for (int i = 1; i <= n; i ++) {
for (int j = i + 1; j <= n; j ++)
while (a[j][i]) {
int div = a[i][i] / a[j][i];
for (int k = i; k <= n; k ++)
a[i][k] = (a[i][k] - 1ll * div * a[j][k] % mod + mod) % mod;
swap(a[i], a[j]), w *= -1;
}
(res *= a[i][i]) %= mod;
}
return (res * w + mod) % mod;
}
long long C(long long x, long long y) {
if (x < 0 || y < 0) return 0;
return c[x][y];
}
void solve() {
n = read(), m = read(), k = read();
c[0][0] = 1;
for (int i = 1; i <= max(n, m) * 4; i ++) {
c[i][0] = 1;
for (int j = 1; j <= i; j ++)
c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
}
for (int i = 0; i < k; i ++)
for (int j = 0; j < k; j ++)
a[i + 1][j + 1] = C(n + m, m + j - i);
cout << solve(a, k) << '\n';
}
signed main() {
int t = 1;
while (t --)
solve();
return 0;
}
QOJ3082
题目描述
You are given integers \(N, M, K, R, C\), and \(V\). Find the number of \(N\) by \(M\) integer matrices \(a=\left(a_{i, j}\right)\) that satisfy all of the following conditions, modulo 998244353.
- \(1 \leq a_{i, j} \leq K\) for all \(1 \leq i \leq N, 1 \leq j \leq M\).
- \(a_{i, j} \leq a_{i, j+1}\) for all \(1 \leq i \leq N, 1 \leq j \leq M-1\).
- \(a_{i, j} \leq a_{i+1, j}\) for all \(1 \leq i \leq N-1,1 \leq j \leq M\).
- \(a_{R, C}=V\).
The first line contains integers \(N, M(1 \leq N, M \leq 200), K(1 \leq K \leq 100), R(1 \leq R \leq N), C (1 \leq C \leq M)\), and \(V(1 \leq V \leq K)\).
Print the answer.
题目
先不考虑第四个条件。将异色之间的分界线作为网格图上的路径。为使得路径之间是严格不交,将第 \(i\) 个初始点和第 \(i\) 个终点向上提高 \(k - i\) 格。容易发现只有 \(k - 1\) 条分界线。
对于 \((0, x) \to (m, y)\) 的路径,方案数是 \(\binom{m}{y - x + m}\)。设 \(e(A, B) = \binom{m}{y_B - y_A + m}\),设 \(A = \{1 \leq i < k \mid (0, k - i)\}, B = \{1 \leq i < k \mid (m, n + k - i)\}\),那么由 LGV 引理,答案就是:
现在考虑 \(a_{R, C} = V\) 对网格图有什么限制。不考虑了,哈哈。
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
long long read() {
long long x = 0, k = 1; char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') k = -1, c = getchar();
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return x * k;
}
const int N = 207;
const int M = 1007;
const int T = 1000;
const long long mod = 998244353;
int n, m, k, r, c, v;
long long a[N][N], C[M][M], px[N][N];
long long solve(long long a[][N], int n) {
long long res = 1, w = 1;
for (int i = 1; i <= n; i ++) {
for (int j = i + 1; j <= n; j ++)
while (a[j][i]) {
int div = a[i][i] / a[j][i];
for (int k = i; k <= n; k ++)
a[i][k] = (a[i][k] - 1ll * div * a[j][k] % mod + mod) % mod;
swap(a[i], a[j]), w *= -1;
}
(res *= a[i][i]) %= mod;
}
return (res * w + mod) % mod;
}
long long P(long long x, long long y) {
swap(x, y);
if (x < 0 || y < 0) return 0;
return C[x][y];
}
long long ksm(long long x, long long y = mod - 2) {
long long res = 1;
for (; y; y >>= 1, (x *= x) %= mod)
if (y & 1) (res *= x) %= mod;
return res;
}
long long gauss(long long t[][N], int n) {
int now = 1, j;
for (int i = 1; i <= n; i ++) {
for (j = now; j <= n; j ++)
if (t[j][i] != 0)
break;
if (j > n)
continue;
for (int k = 1; k <= n + 1; k ++)
swap(t[now][k], t[j][k]);
long long tmp = ksm(t[now][i], mod - 2);
for (int k = 1; k <= n + 1; k ++)
(t[now][k] *= tmp) %= mod;
for (int k = 1; k <= n; k ++) {
if (k == now) continue;
long long p = t[k][i];
for (int l = 1; l <= n + 1; l ++) {
t[k][l] = (t[k][l] - p * t[now][l] % mod + mod) % mod;
}
}
// for (int x = 1; x <= n; x ++, puts(""))
// for (int y = 1; y <= n + 1; y ++)
// cout << t[x][y] << ' ';
// printf("j = %d\n", j);
now ++;
}
return t[v + 1][n + 1];
}
long long wjc1[N][N], wjc2[N][N];
long long get(int i, int j) {
i --, j --;
long long ans = 0;
for (int x = 0; x <= c + v - 2; x ++) {
int y = r + c - x - 1;
if (x < i || y < -i) continue;
if (x > m + j || y > n - j) continue;
(ans += P(x - i, x + y) % mod * P(m + j - x, m - x + n - y) % mod) %= mod;
}
wjc1[i + 1][j + 1] = ans;
ans = 0;
for (int x = c + v; x <= m + k - 1; x ++) {
int y = r + c - x - 1;
if (x < i || y < -i) continue;
if (x > m + j || y > n - j) continue;
(ans += P(x - i, x + y) * P(m + j - x, m - x + n - y) % mod) %= mod;
}
wjc2[i + 1][j + 1] = ans;
return ans;
}
void solve() {
n = read(), m = read(), k = read() - 1, r = read(), c = read(), v = read() - 1;
C[0][0] = 1; r = n - r + 1;
for (int i = 1; i <= T; i ++) {
C[i][0] = 1;
for (int j = 1; j <= i; j ++)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
for (int i = 1; i <= k; i ++)
for (int j = 1; j <= k; j ++)
get(i, j);
for (long long val = 0; val <= k; val ++) {
for (int i = 1; i <= k; i ++)
for (int j = 1; j <= k; j ++)
a[i][j] = (wjc1[i][j] * val % mod + wjc2[i][j]) % mod;
long long tmp = 1;
for (int i = 1; i <= k + 1; i ++, (tmp *= val) %= mod)
px[val + 1][i] = tmp;
px[val + 1][k + 2] = solve(a, k);
}
cout << gauss(px, k + 1) << '\n';
}
signed main() {
int t = 1;
while (t --)
solve();
return 0;
}

浙公网安备 33010602011771号