P7736-[NOI2021]路径交点【LGV引理】

正题

题目链接:https://www.luogu.com.cn/problem/P7736


题目大意

(k)层的图,第(i)层有(n_i)个点,每层的点从上到下排列,层从左到右排列。再给出连接相邻层的一些有向边(从(i)层连向(i+1)层)。
对于(n_1)层每个点作为起点同时出发走到不同的(n_k)层的点的所有路径方案中,交点数量为偶数的减去为奇数的方案有多少个。

(1leq kleq 100,2leq n_1leq 100,n_1=n_k,n_1leq n_ileq 2 imes n_1,1leq Tleq 5)


解题思路

分析一下,若第(1)层起点(1,2)分别对应终点(1,2),记(f_{i,1/2})表示(1/2)在第(i)层的位置。中间某个位置他们路径有了交点,那么一定满足他们存在一层使得(f_{i,1}>f_{i,2})但是因为(f_{1,1}<f_{1,2})(f_{n,1}<f_{n,2})也就是说明前后都各有一个交点。
拓展一下也就是说中间的路径走法不影响交点的奇偶性,只有起点和终点会影响奇偶性。再进一步说,路径交点的奇偶性就是起点对应终点的排列(p_i)的逆序对数量的奇偶性。

设排列(p)表示起点(i)会走到终点(p_i)(sigma(p))表示排列(p)的逆序对数量,(w(p))表示排列(p)的路径方案
那么答案就是

[sum (-1)^{sigma(p)}w(p) ]

发现和(LGV)引理的式子一模一样,直接上就好了

时间复杂度(O(T(k^2n+n^3)))


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<vector>
#define ll long long
using namespace std;
const ll N=210,P=998244353;
ll T,k,n[N],m[N],f[N][N],a[N][N];
vector<int> G[N][N];
ll read(){
	ll x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
ll power(ll x,ll b){
	ll ans=1;
	while(b){
		if(b&1)ans=ans*x%P;
		x=x*x%P;b>>=1;
	}
	return ans;
}
ll det(ll n){
	ll ans=1,f=1;
	for(ll i=1;i<=n;i++){
		for(ll j=i;j<=n;j++)
			if(a[j][i]){
				if(j!=i)swap(a[j],a[i]),f=!f;
				break;
			}
		ll inv=power(a[i][i],P-2);ans=ans*a[i][i]%P;
		for(ll j=i;j<=n;j++)a[i][j]=a[i][j]*inv%P;
		for(ll j=i+1;j<=n;j++){
			ll rate=P-a[j][i];
			for(ll k=i;k<=n;k++)
				(a[j][k]+=rate*a[i][k]%P)%=P;
		}
	}
	return f?ans:((P-ans)%P);
}
signed main()
{
	T=read();
	while(T--){
		k=read();
		for(ll i=1;i<=k;i++)n[i]=read();
		for(ll i=1;i<k;i++)m[i]=read();
		for(ll i=1;i<k;i++){
			for(ll j=1;j<=m[i];j++){
				ll x=read(),y=read();
				G[i][x].push_back(y);
			}
		}
		for(ll i=1;i<=n[k];i++){
			f[k][i]=1;f[k][i-1]=0;
			for(ll j=k-1;j>=1;j--)
				for(ll x=1;x<=n[j];x++){
					f[j][x]=0;
					for(ll p=0;p<G[j][x].size();p++)
						(f[j][x]+=f[j+1][G[j][x][p]])%=P;
				}
			for(ll j=1;j<=n[1];j++)
				a[j][i]=f[1][j];
		}
		f[k][n[k]]=0;
		printf("%lld
",det(n[1]));
		for(ll i=1;i<=k;i++)
			for(ll j=1;j<=n[i];j++)
				G[i][j].clear();
	}
	return 0;
}
原文地址:https://www.cnblogs.com/QuantAsk/p/15126691.html