题目链接:点击这里
题目大意:
有一个
n
×
n
n\times n
n×n 的棋盘,左下角为
(
1
,
1
)
(1,1)
(1,1),右上角为
(
n
,
n
)
(n,n)
(n,n),若一个棋子在点
(
x
,
y
)
(x,y)
(x,y),那么走一步只能走到
(
x
+
1
,
y
)
(x+1,y)
(x+1,y) 或
(
x
,
y
+
1
)
(x,y+1)
(x,y+1)。
现在有 m m m 个棋子,第 i i i 个棋子一开始放在 ( a i , 1 ) (a_i,1) (ai,1),最终要走到 ( b i , n ) (b_i,n) (bi,n) 。问有多少种方案,使得每个棋子都能从起点走到终点,且对于所有棋子,走过路径上的点互不相交。输出方案数 m o d 998244353 \bmod\ 998244353 mod 998244353 的值。
两种方案不同当且仅当存在至少一个棋子所经过的点不同。
题目分析:
L
G
V
LGV
LGV 引理:
G
G
G 是一个有限的带权有向无环图。每个顶点的度是有限的,不存在有向环(所以路径数量是有限的)。
起点
A
=
{
a
1
,
⋯
,
a
n
}
A=\{a_1,\cdots,a_n\}
A={a1,⋯,an},终点
B
=
{
b
1
,
⋯
,
b
n
}
B=\{b_1,\cdots,b_n\}
B={b1,⋯,bn}。
每条边
e
e
e 有权
w
e
w_e
we ,并假定值属于某交换环 。
对于一个有向路径
P
P
P,定义
ω
(
P
)
\omega(P)
ω(P) 为路径上所有边权的积。
对任意顶点
a
a
a,
b
b
b ,定义
e
(
a
,
b
)
=
∑
P
:
a
→
b
ω
(
P
)
e(a,b)=\sum\limits_{P:a \to b}{\omega(P)}
e(a,b)=P:a→b∑ω(P) 。
设矩阵
M
=
(
e
(
a
1
,
b
1
)
e
(
a
1
,
b
2
)
⋯
e
(
a
1
,
b
n
)
e
(
a
2
,
b
1
)
e
(
a
2
,
b
2
)
⋯
e
(
a
2
,
b
n
)
⋮
⋮
⋱
⋮
e
(
a
n
,
b
1
)
e
(
a
n
,
b
2
)
⋯
e
(
a
n
,
b
n
)
)
M= \begin{pmatrix} 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{pmatrix}
M=⎝⎜⎜⎜⎛e(a1,b1)e(a2,b1)⋮e(an,b1)e(a1,b2)e(a2,b2)⋮e(an,b2)⋯⋯⋱⋯e(a1,bn)e(a2,bn)⋮e(an,bn)⎠⎟⎟⎟⎞
那么
A
A
A 到
B
B
B 的所有不相交的路径条数为
det
(
M
)
\det(M)
det(M)
对于本题:
由于是求方案数所以可以把边权抽象成
1
1
1
e
(
a
i
,
b
j
)
e(a_i,b_j)
e(ai,bj) 可以利用排列组合来计算,就是从
(
a
i
,
1
)
(a_i,1)
(ai,1) 走到
(
b
j
,
n
)
(b_j,n)
(bj,n) 的方案数,即
C
b
j
−
a
i
+
n
−
1
n
−
1
C_{b_j-a_i+n-1}^{n-1}
Cbj−ai+n−1n−1
然后套一个高斯消元求行列式即可
时间复杂度为
O
(
n
+
m
3
)
O(n+m^3)
O(n+m3)
具体细节见代码:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<queue>
#define ll long long
#define inf 0x3f3f3f3f
//#define int ll
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
int read()
{
int res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 2e6+5;
const int mod = 998244353;
const double pi = acos(-1);
const double eps = 1e-8;
int n,m,a[maxn],b[maxn],fac[maxn],inv[maxn],mat[105][105];
int qpow(int a,int b)
{
int res = 1;
while(b)
{
if(b&1) res = (ll)res*a%mod;
a = (ll)a*a%mod;
b >>= 1;
}
return res;
}
int Gauss(int a[][105],int n,const int p) // 求行列式
{
int res = 1;
for(int i = 1;i <= n;i++)
{
int pos = i;
for(int j = i+1;j <= n;j++)
if(a[j][i] > a[pos][i]) pos = j;
if(!a[pos][i]) return 0;
if(i^pos) swap(a[i],a[pos]),res *= -1;
for(int j = i+1;j <= n;j++)
{
if(a[j][i] > a[i][i]) swap(a[j],a[i]),res *= -1;
while(a[j][i])
{
int tmp = a[i][i]/a[j][i];
for(int k = i;k <= n;k++)
a[i][k] = (a[i][k]+(ll)(p-tmp)*a[j][k])%p;
swap(a[j],a[i]),res *= -1;
}
}
}
for(int i = 1;i <= n;i++) res = (ll)res*a[i][i]%p;
return (res+p)%p;
}
int C(int n,int m)
{
if(n < m) return 0;
return (ll)fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
fac[0] = 1;
for(int i = 1;i < maxn;i++) fac[i] = (ll)fac[i-1]*i%mod;
inv[maxn-1] = qpow(fac[maxn-1],mod-2);
for(int i = maxn-1;i;i--) inv[i-1] = (ll)inv[i]*i%mod;
int t = read();
while(t--)
{
n = read(),m = read();
for(int i = 1;i <= m;i++) a[i] = read(),b[i] = read();
for(int i = 1;i <= m;i++)
for(int j = 1;j <= m;j++) mat[i][j] = C(b[j]-a[i]+n-1,n-1);
cout<<Gauss(mat,m,mod)<<endl;
}
return 0;
}