HDU 5852 Intersection is not allowed【容斥+行列式求解】

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5852

题意:

给定$n*n$的棋盘,及$m$个棋子的初始位置及终点,初始均在第$1$行,终点在第$n$行,每次只能向下或者右移动,每步只能往下或往右移,要求移动路径不能交叉,求路径方案数。
数据范围:$n \le 100000, m \le 100$

分析:

Lindström–Gessel–Viennot引理
引用大佬:

题目类型貌似很单一,通常都是上面有点集A,下面有点集B,然后求每个A到对应B的不相交路径总数
可能还会有一些限制QAQ
然后我们考虑我们求出f(i,j)表示A的第i个点到B的第j个点的路径方案
考虑如果设排列P,我们考虑i->P(i)
则这个排列有多少个逆序对就至少有多少个交点
这里出现了至少,我们可以考虑容斥原理
写出容斥原理的式子之后我们会得到一个O(n!)的算法
之后仔细观察会发现容斥原理的式子实际上就是求行列式
然后O(n^3)求行列式即可

行列式的定义:
设$A$是数域上的一个$n$阶方阵,从$A$中取出不同行又不同列的$n$个元素做乘积$(-1)^{t(j_1,j_2…j_n)}a_{1j_1}a_{2j2}…a_{nj_n}$构成一项,这样的项恰有$n!$个,称这$n!$项之和为$A$的行列式。即$|A|=\sum_{j_1,j_2,…j_n} (-1)^{t(j_1,j_2…j_n)}a_{1j_1}a_{2j_2}…a_{nj_n}$
根据定义我们发现容斥求解的过程就是在求行列式= =
求行列式就是化为上三角矩阵,答案即为对角线元素的乘积。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*************************************************************************
> File Name: 5852.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
> Created Time: 2016/10/9 23:31:42
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
#include<iostream>
#include<cstdio>
#include<cmath>
typedef long long ll;
using namespace std;
const int maxn = 1e2 + 5, maxm = 1e5 + 5, mod = 1e9 + 7;
ll a[maxn][maxn];
int sign;
int N;
ll solve()
{
ll ans = 1;
for(int i = 0; i < N; i++){//当前行
for(int j = i + 1; j < N; j++){//当前之后的每一行,因为每一行的当前第一个数要转化成0(想想线性代数中行列式的计算)
int x = i, y = j;
while(a[y][i]){//利用gcd的方法,不停地进行辗转相除
ll t = a[x][i] / a[y][i];
for(int k = i; k < N; k++)
a[x][k] = (a[x][k] - a[y][k] * t) % mod;
swap(x,y);
}
if(x != i){//奇数次交换,则D=-D'整行交换
for(int k = 0; k < N; k++)
swap(a[i][k], a[x][k]);
sign ^= 1;
}
}
//斜对角中有一个0,则结果为0
if(a[i][i] == 0) return 0;
else ans = ans * a[i][i] % mod;
}
if(sign) ans *= -1;
if(ans < 0) ans += mod;
return ans;
}
int x[maxn];
int y[maxn];
ll quick_pow(ll a, int b, int mod)
{
ll ans = 1;
for(; b; b >>= 1, a = a * a % mod){
if(b & 1) ans = ans * a % mod;
}
return ans;
}
ll fact[2 * maxm + 5];
ll rev[2 * maxm + 5];
int main()
{
int T;scanf("%d", &T);
fact[0] = 1; rev[0] = 1;
for(int i = 1; i < 2 * maxm; ++i) fact[i] = fact[i - 1] * 1ll * i % mod;
for(int i = 1; i < 2 * maxm; ++i) rev[i] = quick_pow(fact[i], mod - 2, mod);
while(T--){
sign = 0;
int n;scanf("%d%d", &n, &N);
for(int i = 0; i < N; ++i){
scanf("%d", &x[i]);
}
for(int i = 0; i < N; ++i){
scanf("%d", &y[i]);
}
for(int i = 0; i < N; ++i){
for(int j = 0; j < N; ++j){
if(y[j] < x[i]) a[i][j] = 0;
else{
a[i][j] = fact[n - 1 + y[j] - x[i]] * rev[n - 1] % mod * rev[y[j] - x[i]] % mod;
}
}
}
printf("%lld\n", solve() % mod);
}
return 0;
}
文章目录
  1. 1. 题目链接:
  2. 2. 题意:
  3. 3. 分析:
  4. 4. 代码: