luogu P7437 既见君子
题面传送门
很奇怪为什么这么水的题目能到黑题。
因为它有一条\(1\)到\(n\)且必须经过\(k\)的路径,所以我们考虑状压dp出路径条数然后再矩阵树定理。
设\(dp_{i,j}\)为已经经过了\(i\)集合的点,且最后一个点为\(j\)的方案数,这个东西是平凡的\(O(2^nn^2)\)
然后对于每个\(dp_{i,n}\),我们将这个链缩成一个点然后直接矩阵树定理即可。时间复杂度\(O(2^{n-3}n^3)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 20
#define M 100000
#define W (1<<20)
#define mod 998244353
#define eps (1e-5)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
using namespace std;
int n,m,k,x,y,now,cnt;ll D[N+5][N+5],G[N+5][N+5],A[N+5][N+5],dp[(1<<N)+5][N+5],fl[N+5],ToT;
I int calc(){
re int i,j,h;ll Ans=1;for(i=1;i<cnt;i++){
for(j=i+1;j<cnt;j++){
while(A[j][i]){
now=mod-A[i][i]/A[j][i];for(h=i;h<cnt;h++)A[i][h]=(A[i][h]+A[j][h]*now)%mod,swap(A[i][h],A[j][h]);Ans*=-1;
}
}
}for(i=1;i<cnt;i++) Ans=Ans*A[i][i]%mod;return (Ans+mod)%mod ;
}
I ll mpow(ll x,int y=mod-2){ll ans=1;while(y) (y&1)&&(ans=ans*x%mod),x=x*x%mod,y>>=1;return ans;}
int main(){
freopen("1.in","r",stdin);
re int i,j,h;scanf("%d%d%d",&n,&m,&k);for(i=1;i<=m;i++) scanf("%d%d",&x,&y),D[x][x]++,D[y][y]++,G[x][y]++,G[y][x]++;dp[1][1]=1;
m=1<<n;for(i=3;i<m;i+=2){
for(j=1;j<=n;j++){
if(!((i>>j-1)&1))continue;
for(h=1;h<=n;h++){
if(j==h||!((i>>h-1)&1))continue;dp[i][j]=(dp[i][j]+dp[i^(1<<j-1)][h]*G[j][h])%mod;
}
}
}
for(i=1;i<(1<<n);i+=2){
if(!((i>>k-1)&1)||!((i>>n-1)&1)) continue;
for(j=1;j<=n;j++) fl[j]=!((i>>j-1)&1);for(cnt=0,j=1;j<=n;j++) fl[j]=(fl[j]?++cnt:0);if(cnt^n) for(cnt++,j=1;j<=n;j++)!fl[j]&&(fl[j]=cnt);
Me(A,0);for(j=1;j<=n;j++) for(h=j+1;h<=n;h++) A[fl[j]][fl[j]]+=G[j][h],A[fl[h]][fl[h]]+=G[j][h],A[fl[j]][fl[h]]-=G[j][h],A[fl[h]][fl[j]]-=G[j][h];
ToT=(ToT+calc()*dp[i][n])%mod;
}cnt=n;for(i=1;i<=n;i++) for(j=1;j<=n;j++) A[i][j]=D[i][j]-G[i][j];printf("%lld\n",ToT*mpow(calc())%mod);
}