【题解】Luogu P4321 随机漫游

看到 \(n\le 18\),基本上是要做状压的。考虑进行预处理,然后在较小的复杂度内回答询问。设 \(f_{S,u}\) 表示当前走完了 \(S\) 中的点,现在在 \(u\) 点(\(u\in S\)),走完剩下的点的期望步数。于是有方程:

\[f_{S,u}=1+\frac{1}{d_u}\sum f_{S\cup\{v\},v} \]

其中 \((u,v)\) 是一条边,\(d_u\) 表示 \(u\) 的度数。

考虑 \(S\)\(S\cup\{v\}\) 的关系,显然只有两种情况,分别是 \(S=S\cup\{v\}\)\(S\subsetneqq S\cup\{v\}\)。于是就可以倒着扫 \(S\),对于每个 \(S\) 做高斯消元了。

考虑输出答案,题目的要求其实就是要将 \(c\) 中的点全部走完。设 \(c\) 中的点构成的集合为 \(S\),起点为 \(u\),那么答案即为 \(f_{(\complement S)\cup\{u\},u}\)。总时间复杂度为 \(O(2^nn^3+m)\)

#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int mod=998244353;
int n,m,q,a[25][25];
int f[(1<<18)+5][25];
vector<int> e[25];
il int qpow(int x,int y){
//	cout<<x<<" "<<y<<"\n";
	int res=1;
	while(y){
		if(y&1){
			res=res*1ll*x%mod;
		}
		y>>=1,x=x*1ll*x%mod;
	}
	return res;
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
//	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		e[u].pb(v),e[v].pb(u);
	}
	int uS=(1<<n)-1;
	for(int S=uS-1;S;S--){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n+1;j++){
				a[i][j]=0;
			}
		}
		for(int u=1,tmp;u<=n;u++){
			if(S>>(u-1)&1){
				a[u][u]=mod-1;
				tmp=qpow(e[u].size(),mod-2);
				for(int v:e[u]){
					if(S>>(v-1)&1){
						a[u][v]=tmp;
					}
					else{
						a[u][n+1]=(a[u][n+1]-f[S|1<<(v-1)][v]+mod)%mod;
					}
				}
				a[u][n+1]=(a[u][n+1]*1ll*tmp+mod-1)%mod;
			}
		}
		for(int i=1,cur,tmp;i<=n;i++){
			if(S>>(i-1)&1){
				tmp=0;
				for(int j=i;j<=n;j++){
					if(S>>(j-1)&1){
						if(tmp<a[j][i]){
							tmp=a[j][i],cur=j;
						}
					}
				}
				swap(a[i],a[cur]);
				tmp=qpow(a[i][i],mod-2);
				for(int j=1;j<=n;j++){
					if(i!=j&&(S>>(j-1)&1)){
						for(int k=i+1;k<=n+1;k++){
							if((S>>(k-1)&1)||k==n+1){
								a[j][k]=(a[j][k]-a[j][i]*1ll*a[i][k]%mod*tmp%mod+mod)%mod;
							}
						}
					}
				}
			}
		}
		for(int i=1;i<=n;i++){
			if(S>>(i-1)&1){
				f[S][i]=a[i][n+1]*1ll*qpow(a[i][i],mod-2)%mod;
			}
		}
	}
	cin>>q;
	while(q--){
		int num,S=0,u;
		cin>>num;
		while(num--){
			cin>>u;
			S|=1<<(u-1);
		}
		cin>>u;
		cout<<f[(uS^S)|1<<(u-1)][u]<<"\n";
	}
	return 0;
}
}
int main(){return asbt::main();}
posted @ 2025-03-08 08:40  zhangxy__hp  阅读(9)  评论(0)    收藏  举报