[NOI2021] 路径交点 题解
[NOI2021] 路径交点 题解
题意
给定一张
现在要选出
我们规定,第
现让你求出有偶数个交点的路径方案数比有奇数个交点的路径方案数多多少个。
思路
首先,上面的“异号”这一条件完全可以转化为逆序对。奇偶,做差,逆序对……好像行列式欸。事实上,如果只有两层,答案就是行为左部点,列为右部点的行列式的值。我们从求行列式值的层面来考虑。一个
其中
我们来考虑这里连乘的含义,其实就是在用左部点去匹配右部点;而这里的逆序对,也就是题目中说的交点数量(这里可以自己画一个行列式理解一下)。那么,最后行列式的值,就是方案数之差。
现在来考虑
- 当总交点数为偶数时,则应有
, 异号且 , 异号;或 , 同号且 , 同号。而这两种情况下, 与 均为同号。 - 当总交点数为奇数时,类似的,总有
与 异号。
也就是说交点的奇偶性并不会改变,这一性质同样可以推广到连续 层。
对于任意相邻的两层,我们都可以建立一个行为左部点,列为右部点的邻接矩阵,我们考虑将这
代码:
#include<bits/stdc++.h> using namespace std; const int N = 205; const int mod = 998244353; inline int read(){ int x = 0; char ch = getchar(); while(ch<'0' || ch>'9') ch = getchar(); while(ch>='0'&&ch<='9') x = x*10+ch-48, ch = getchar(); return x; } inline int fpow(int a, int b){ int ret = 1; a%=mod; while(b){ if(b & 1){ ret = (1ll*ret*a)%mod; } a = (1ll*a*a)%mod; b>>=1; } return ret; } struct mat{ int f[N][N]; int h, w; mat(){memset(f, 0, sizeof(f));} int *operator [](int x){return f[x];} void init(int th, int tw){ h = th, w = tw; } void frs(){ memset(f, 0, sizeof(f)); } mat operator *(mat B){ mat t, BT; int wb = B.w, hb = B.h; for(int i = 1; i<=hb; ++i){ for(int j = 1; j<=wb; ++j){ BT[j][i] = B[i][j]; } } for(int i = 1; i<=h; ++i){ for(int j = 1; j<=wb; ++j){ for(int k = 1; k<=w; ++k){ t[i][j] = (1ll*t[i][j]+1ll*f[i][k]*BT[j][k]%mod)%mod; } } } t.init(h, wb); return t; } int calc_det(){ int ret = 1; for(int i = 1; i<=h; ++i){ if(!f[i][i]){ for(int j = i+1; j<=h; ++j){ if(f[j][i]){ swap(f[i], f[j]); ret = -ret; break; } } } int inv = fpow(f[i][i], mod-2); for(int j = i+1; j<=h; ++j){ int tmp = 1ll*f[j][i]*inv%mod; for(int k = i; k<=h; ++k){ f[j][k] = (1ll*f[j][k]-1ll*f[i][k]*tmp%mod)%mod; f[j][k] = (1ll*f[j][k]+mod)%mod; } } } for(int i = 1; i<=h; ++i){ ret = (1ll*ret*f[i][i]%mod); } ret = (1ll*ret+mod)%mod; return ret; } void print(){ for(int i = 1; i<=h; ++i, puts("")){ for(int j = 1; j<=w; ++j){ printf("%d ", f[i][j]); } } } }a[N]; int K; int m[N], n[N]; int T; int main(){ T = read(); while(T--){ K = read(); for(int i = 1; i<=K; ++i){ n[i] = read(); } for(int i = 1; i<K; ++i){ m[i] = read(); } for(int i = 1; i<K; ++i){ a[i].init(n[i], n[i+1]); a[i].frs(); for(int j = 1; j<=m[i]; ++j){ int u = read(), v = read(); ++a[i][u][v]; } } mat ans = a[1]; for(int i = 2; i<K; ++i){ ans = ans*a[i]; } printf("%d\n", ans.calc_det()); } return 0; }