Lindström–Gessel–Viennot Lemma

Lindström–Gessel–Viennot Lemma

作为一个特殊情况,引理提供对给定一个起点终点对集(记起点集为 $A$ 终点集为 $B$ ,且路径为 $ a_i \to b_i $ ),统计不相交路径数。

引理的内容如下:

令 $ \omega(P) := $ 路径 $ P $ 的边权积, $ e(a,b) := \sum_{P:a\to b} \omega(P) $ ,

$$ M := \left( \begin{matrix} e(a_1,b_1) & e(a_1,b_2) & \cdots & e(a_1,b_m) \\ e(a_2,b_1) & e(a_2,b_2) & \cdots & e(a_2,b_m) \\ \vdots & \vdots& \ddots & \vdots \\ e(a_n,b_1) & e(a_n,b_2) & \cdots & e(a_n,b_m) \end{matrix} \right) $$

该引理宣称,$ \det|M| $ 给出$ A $ 到 $ B $ 不相交路径的权值和。

特殊情况,令各边边权为$1$,此时$|M|$为不相交路径数。

更一般情况,$ \omega $ 可以是形式变量,则 $e$ 将成为形式幂级数。

假定我们在网格图中,从第 $1$ 行的起点走到第 $n$ 的终点。

首先假设我们只有两对点 $ (a_1,b_1),(a_2, b_2) $ ,不考虑相交,则总路径数为 $ {b_1-a_1+n-1\choose n-1}{b_2-a_2+n-1 \choose n-1} $ .

考虑 $ a_1\to b_1, a_2 \to b_2 $ 相交,则在最后一个相交点之后交换路径,则得到 $ a_1\to b_2,a_2 \to b_1 $ 的路径。

考虑 $ a_1 \to b_2, a_2 \to b_1 $ 的路径,其必相交,则在最后一个相交点之后交换路径。

此时我们建立了 $ a_1 \to b_1, a_2 \to b_2 $ 的相交路径与 $ a_1 \to b_2, a_2 \to b_1 $ 的一一对应。

所以除去不合法情况后,答案为 $ {b_1-a_1+n-1\choose n-1}{b_2-a_2+n-1 \choose n-1} - {b_1-a_2+n-1\choose n-1}{b_2-a_1+n-1 \choose n-1} $ .

接着考虑多组路径,使用容斥原理。

假设某些路径相交,则在最后一个相交点之后交换路径,得到 $ B $ 的重排 $ C $ .

所以我们只需考虑 $ C $ 的逆序对数,为奇数则贡献为负,否则为正。

展开写,总的式子即为

$$ \sum _{C \subset B\text{的重排}} (-1)^{\tau(C)} \prod_{i=1}^n {c_i-a_i+n-1\choose n-1}. $$

注意到这就是一个行列式。

把对应的组合数换为路径数的记号的话,就是引理的内容了,证明同理。

在学习 Matrix Tree 定理的时候看到这个东西。觉得很妙。

思考理解方式跟之很像。

Codeforces 348D

在有障碍网格图中,统计多少从 $(1,1)$ 到 $(n,m)$ 的不交路径对。

Sol.

对 $ A = \{(1,2),(2,1)\}, B = \{(n-1,m),(n,m-1)\} $ 使用 LGV 引理即可。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template<class T> void read(T& x)
{
    char c = getchar(); T p = 1, n = 0;
    while(c < '0' || c > '9') {if(c == '-') p = -1; c = getchar();}
    while(c >= '0' && c <= '9'){n = n * 10 + c - '0'; c = getchar();}
    x = p * n;
}
template<class T,class U>void read(T&x,U&y){read(x),read(y);}
template<class T,class U,class V>void read(T&x,U&y,V&z){read(x),read(y),read(z);}
const int maxn = 3e3 + 100, mod = 1e9 + 7;
char s[maxn][maxn];
long long dp[maxn][maxn];
int main()
{
    int n, m; read(n, m);
    for(int i = 0; i < n; ++i) scanf("%s", &s[i]);
    if(s[0][1] != '#') dp[0][1] = 1;
    for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) if(s[i][j] != '#')
    {
        if(i > 0) (dp[i][j] += dp[i - 1][j]) %= mod;
        if(j > 0) (dp[i][j] += dp[i][j - 1]) %= mod;
    }
    long long a = dp[n - 2][m - 1], b = dp[n - 1][m - 2];

    memset(dp, 0, sizeof dp);
    if(s[1][0] != '#') dp[1][0] = 1;
    for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) if(s[i][j] != '#')
    {
        if(i > 0) (dp[i][j] += dp[i - 1][j]) %= mod;
        if(j > 0) (dp[i][j] += dp[i][j - 1]) %= mod;
    }
    long long c = dp[n - 2][m - 1], d = dp[n - 1][m - 2];
    printf("%d\n", (a * d % mod - b * c % mod + mod) % mod);
    return 0;
}

HDU 5852

给网格图。只能向下和向右走。给出第一行和第 $ n $ 行的 $ k $ 组对应的起点和终点,保证对应递增。问不相交路径方案数。

Sol.

直接用 LGV 就好了。

用组合数来算路径,以及 Gaussian Elimination 的时候要注意是在模意义下,别傻傻用浮点数。

Code (296ms, 4944kB)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template<class T> void read(T& x)
{
    char c = getchar(); T p = 1, n = 0;
    while(c < '0' || c > '9') {if(c == '-') p = -1; c = getchar();}
    while(c >= '0' && c <= '9'){n = n * 10 + c - '0'; c = getchar();}
    x = p * n;
}
template<class T,class U>void read(T&x,U&y){read(x),read(y);}
template<class T,class U,class V>void read(T&x,U&y,V&z){read(x),read(y),read(z);}
const int maxn = 100 + 100, mod = 1e9 + 7, maxm = 2e5 + 10;
long long a[maxn][maxn];
long long fac[maxm], inv[maxm];
inline long long power(long long a, long long b)
{
    long long ret = 1;
    for(; b; b >>= 1, (a *= a) %= mod) if(b & 1)
        (ret *= a) %= mod;
    return ret;
}
inline long long C(long long a, long long b){return fac[a] * inv[b] % mod * inv[a - b] % mod;}
int k;
long long det()
{
    long long ret = 1, sgn = 0;
    for(int i = 0; i < k; ++i)
    {
        int t = i;
        for(; a[t][i] == 0; ++t);
        if(t >= k) return 0;
        for(int j = i; j < k; ++j) swap(a[i][j], a[t][j]);
        sgn *= -1;
        long long inv = power(a[i][i], mod - 2);
        for(int j = i + 1; j < k; ++j) if(a[j][i] != 0)
        {
            long long c = a[j][i] * inv % mod;
            for(int l = i; l < k; ++l) ((a[j][l] -= c * a[i][l] % mod) += mod) %= mod;
        }
        (ret *= a[i][i]) %= mod;
    }
    return (ret + mod) % mod;
}
int s[maxn], t[maxn];
int main()
{
    int t; read(t);
    fac[0] = 1; for(int i = 1; i < maxm; ++i) fac[i] = fac[i - 1] * i % mod;
    inv[maxm - 1] = power(fac[maxm - 1], mod - 2);
    for(int i = maxm - 2; ~i; --i) inv[i] = inv[i + 1] * (i + 1) % mod;
    while(t--)
    {
        memset(a, 0, sizeof a);
        int n; read(n, k);
        for(int i = 0; i < k; ++i) read(s[i]);
        for(int j = 0; j < k; ++j) read(::t[j]);
        for(int i = 0; i < k; ++i) for(int j = 0; j < k; ++j) if(s[i] <= ::t[j])
            a[i][j] = C(::t[j] - s[i] + n - 1, n - 1);
        printf("%I64d\n", det());
    }
    return 0;
}

Reference

Wikipedia

Game Theory

Game Theory

Impartial Combinatorial Games

公平组合游戏指

  1. 两个玩家均采用对自己最有利的决策,且轮流做出决策;
  2. 玩家知道之前的所有决策;
  3. 游戏的任意时刻,不含随机成分;
  4. 结局只有输和赢,没有平局.

Nim

定义.

Sprague-Grundy Theorem

DAG. mex.

SG 函数三个性质

  1. 若 $ \text{SG}(x)=0 $ 则 $ x $ 所有后继结点都不为 $ 0 $ ;
  2. 若 $ \text{SG}(x)\neq0 $ 则 $ x $ 至少有一个后继结点为 $ 0 $ ;
  3. 结束状态的 SG 值为 $ 0 $ .

游戏的和.

Theorem 对若干个游戏图 $ (G_i)_{i=1}^n $ ,有

$$ \text{SG}(\sum_i G_i)=\oplus_i\text{SG}(G_i) $$

Proof. 设 $ G:=\sum_iG_i $ ,考虑 $ G $ 的所有可能转移

$$ G_1+G_2+\cdots+G'_i+\cdots+G_n,\forall1\le i\le n $$

其中 $ G_i' $ 为 $ G_i $ 的转移. 设 $ F_G:= G$ 的所有转移的集合,$ b:=\oplus_i\text{SG}(G_i) $ ,等价要证

  1. $ \forall a<b,\exist g\in F_G,\text{SG}(g)=a $ ;
  2. $ \forall g \in F_G, \text{SG}(g)\neq b $ .

对 1. 设 $ d:=b\oplus a , d$ 的最高位为 $ k $ ,则一定存在 $ \text{SG}(G_i) $ 满足第 $ k $ 位为 $ 1 $ . 因为 $ \text{SG}(G_i)\oplus d<\text{SG}(G_i) $ ,由定义 $ \exist G_i'\in F_{G_i} : \text{SG}(G_i')=\text{SG}(G_i)\oplus d$ . 由 $ d=b\oplus a \Leftrightarrow a=d\oplus b $ , 有 $ a=\text{SG}(G_1)\oplus \text{SG}(G_2) \oplus \cdots \oplus \text{SG}(G_i) \oplus d \oplus \cdots \oplus \text{SG}(G_n) = \cdots \oplus \text{SG}(G_i') \oplus \cdots $ .

对 2 用反证法. 若存在则有 $ b = \text{SG}(G_1)\oplus \text{SG}(G_2) \oplus \cdots \oplus \text{SG}(G_i') \oplus \cdots \oplus \text{SG}(G_n) = \text{SG}(G_1)\oplus \text{SG}(G_2) \oplus \cdots \oplus \text{SG}(G_i) \oplus \cdots \oplus \text{SG}(G_n) $ 即 $ \text{SG}(G_i')=\text{SG}(G_i) $ ,与定义矛盾. $ \blacksquare $

Misère Nim

取到最后一个石子的输. 其余同 Nim.

Theorem 先手必胜条件:

  1. SG 和为 $ 0 $ 且不存在一个石堆中石子的数量大于 $ 1 $ ;
  2. SG 和不为 $ 0 $ 且存在一个石堆中石子的数量大于 $1$ .

Proof. 对 1 显然.

对 2 ,如果大于 $ 1 $ 的石堆只有一个,那么 SG 和大于 $ 0 $ . 根据其他石堆的奇偶性决定将当前石堆取剩几个.

如果有多个大于 $ 1 $ 的石堆有多个,因为 SG 和大于 $ 0 $ ,可以按照常规操作使得局面只剩一个。如果 SG 等于 $ 0 $ 的话,一次操作后大于 $ 0 $ ,相当于给了对手必胜态. $ \blacksquare $

Moore's Nim$_k$

每次可以在至多 $ k $ 堆石子中取任意多个石子. 其余同 Nim.

Theorem 考虑 $ n $ 堆石子的二进制展开,令 $ x_i:= $ 表示第 $ i $ 堆石子的数量,设 $ x_i=\sum_{j=0}^m g_{i,j} 2^j $ ,如果满足 $ \forall 0\le j \le m, \sum_{i=0}^{n-1}g_{i,j}\equiv 0\pmod {k+1} $ ,那么先手必败,否则先手必胜.

Proof.

Misère Moore's Nim$_k$

取到最后一个石子输. 其余同 Moore's Nim$_k$ .

Theorem 先手必胜条件:

  1. 当不存在 $ > 1 $ 的石堆的时候,堆数 $ \mod k+1 \neq 1 $ ;
  2. 当存在 $ >1 $ 的石堆的时候,存在某一列和 $ \mod k+1\neq 0 $ .

Proof.

Staircase

有 $ n $ 层楼梯,编号 $ 1..n $ ,每层阶梯上有一个硬币. 现在有两个玩家轮流操作,每次可以将第 $ j $ 层阶梯上任意多个(至少一个)硬币放到 $ j-1 $ 层阶梯上,第 $ 0 $ 层为地板. 最后一枚放到地板上的玩家胜利.

Theorem 将奇数层硬币数视为 Nim 堆即可.

Proof. 先手按 Nim 策略移动奇数层硬币。对方有两种策略,应对也如下:

  1. 对方移动奇数层,则继续移动奇数层;
  2. 对方移动偶数层,则将其偶数层的移动的奇数层.

这样相当于找到了无效化对方操作的构造. 之所以是奇数层是因为地板为 $ 0 $ 层.

Wythoff Game

Take-and-Break

允许拿走或者特定情况下将一堆分为若干堆.

其实有很多Take-and-Break 这一类型的游戏,但是大多都要打表找规律,甚至根本就没规律.

Variant - Lasker's Nim

每次可以从一堆中拿走若干个石子,或者将一堆至少有两个石子的石堆分裂成两个非空石堆.

Variant - Grundy's Game

每次可以将一堆石子分成两堆大小不同的非空石子.

Ideas

Ideas

  1. 使用 trie 搭建平衡树。
  2. 牛顿迭代手算开根
  3. 树上,每 $ \sqrt n $ 个点有一个关键点,一个点的信息被最近的祖先关键点维护。

Inclusion-Exclusion Principle

Inclusion-Exclusion Principle

Background

Operation

Mobius Function

Formal

$$ |\bigcup_{i=1}^n A_i|=\sum_{\empty \neq T\subset [n]}(-1)^{|T|-1}|\bigcap_{t\in T} A_t| $$

Maximum-minimums Identity

$$ \max_{i=1}^n A_i=\sum_{\empty\neq T\sub[n]}(-1)^{|T|-1}\min_{t\in T}A_t $$

一种角度是表示 $ A_i $ 为集合 $ 1,2,\cdots,X_i $ (类似于 0/1 表示取或不取,那每一位 min/max 和 交/并 是等价的),将 $ \max A_i $ 视为 $ |\cup A_i| $ ,$ \min A_i $ 视为 $ |\cap A_i| $ .

另一种角度是将右式展开,发现所有 $ 2^n-1 $ 个子集中除了 $ \max A_i $ 外都被两两抵消:考虑一个不是 $\max A_i$ 的数,那么考虑这个数作为最小值贡献在右式的情况,容易得到比它大奇数大小和偶数大小的集合数是相同的,最后只剩下 $ \max A_i $ 没有集合可以配对,剩下自己。

这启发我们将这视为一个反演:容易看出反演核为 $ \sum_{\empty\neq T\sub U\sub[n]}(-1)^{|T|+|U|}=[U=[n]] $ .

Inversion Theory

Inversion Theory

反演理论。

本文将首先给出几个例子,迅速熟悉反演现象。接着从理论的高度对此做深入分析和拓展。

Bi-coefficient Inversion

我们有二项式变换、反演式:

$$ g(n): =\sum_{k=0}^n {n\choose k} f(k) \\ f(n) =\sum_{k=0}^n (-1)^{n-k}{n\choose k}g(k) $$

将后(反演)式带入前(变换)式即可证明。证明之中我们会注意到 $ \sum_{k=0}^n (-1)^k {n\choose k} = [n=0] $.

$ n $ 个数重排列,求不含有不动点的排列数。

Mobius Inversion

Transformation, Inversion

我们已知

$$ g(n)=\sum_{k:p(k)} a_{n,k} f(i) $$

这称为 $ f $ 的变换式。其中 $ p(k) $ 为变换下标需满足的一个命题。已知 $ f $ 求 $ g $ 十分简单,反过来的过程,即变换的逆运算,称反演

形式化而言,反演指找出 $ b, $ s.t.

$$ f(n)=\sum_{k:p(k)}b_{n,k}g(i) $$

使用线性代数的观点来看,变换矩阵 $ A:A_{n,k}:=a_{n.k} $ 为下三角矩阵,而反演矩阵 $ B:BA=I $ ,即变换矩阵、反演矩阵互为逆矩阵。

由于学术界没有约定术语,很多地方变换、反演不做严格区分。

找出反演矩阵的难度不高于求逆矩阵。我们会总是根据一些特殊的性质来简化某些特定的反演变换式,我们也只关心存在有效简化的式子。

Inversion Kernel

注意到命题函数 $ [p(k)=0] $ 总存在于以上两个反演变换的证明中,我们对此感到好奇。

我们会注意到其实这也就是单位矩阵 $ I $ 的表达。

考虑 $ k \le n $,我们有

$$ f(n)=\sum_{k=1}^n a_{n,k}g(k) $$

由于这是线性的,相当于我们可以对每一个 $ f(m) $ 计算贡献。对每一个$ m $ 求出使 $ f(m) = 1 $ 而其他 $ f $ 都为 $ 0 $ 的 $ g $ 即可,用 $ \mu(n,m) $ 表示这个解。

那么一定有

$$ \sum_{k=1}^na_{n,k}\mu(k,m)=[n=m] $$

这就是我们在推反演式要用到的重要式子,称反演核

显然可以 $ O(n^2) $ 递推求出,和求逆矩阵复杂度一致。如果 $ \mu $ 没有特殊的性质那这就没什么意思了。

Fourier Transformation

On Preceding with GCD, LCM

Convolution

$$ c_r=\sum_{p,q}[f(p,q)=r]a_pb_q $$