P4321随机漫游【状压dp,数学期望,高斯消元】

正题

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


题目大意

给出\(n\)个点\(m\)条边的一张无向图,\(q\)次询问。

每次询问给出一个点集和一个起点,求从起点出发随机游走经过所有点集的期望步数。

\(n\in[1,18],m\in[1,\frac{n(n-1)}{2}],q\in[1,10^5]\)


解题思路

首先\(n\)很小可以状压经过点的状态,然后因为这个询问是给出起始状态所以需要倒推。设\(f_{s,x}\)表示目前状态是\(s\),在点\(x\),覆盖所有点的期望次数。

那么有方程

\[f_{S,x}=\sum_{x->y}f_{S\cap y,y} \]

然后\(S\)不同的当常数,相同的高斯消元转移即可。

时间复杂度\(O(2^nn^3)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=19,M=1e5+10,P=998244353;
ll n,m,q,inv[M],deg[N],a[N][N],f[1<<N][N];
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;
}
namespace G{
	ll a[N][N],b[N];
	void clear(){
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		return;
	}
	void solve(ll *f){
		for(ll i=1;i<=n;i++){
			ll p=i;
			for(ll j=i;j<=n;j++)
				if(a[j][i]){p=j;break;}
			swap(a[i],a[p]);swap(b[i],b[p]);
			ll inv=power(a[i][i],P-2);
			for(ll j=i;j<=n;j++)
				a[i][j]=a[i][j]*inv%P;
			b[i]=b[i]*inv%P;
			for(ll j=i+1;j<=n;j++){
				int rate=P-a[j][i];
				for(ll k=i;k<=n;k++)
					a[j][k]=(a[j][k]+a[i][k]*rate%P)%P;
				b[j]=(b[j]+b[i]*rate%P)%P;
			}
		}
		for(ll i=n;i>=1;i--){
			for(ll j=i+1;j<=n;j++)
				b[i]=(b[i]-a[i][j]*b[j]%P+P)%P;
			f[i]=b[i];
		}
		return;
	}
}
signed main()
{
	scanf("%lld%lld",&n,&m);inv[1]=1;
	for(ll i=2;i<=m;i++)
		inv[i]=P-(P/i)*inv[P%i]%P;
	for(ll i=1;i<=m;i++){
		ll x,y;
		scanf("%lld%lld",&x,&y);
		a[x][y]++;a[y][x]++;
		deg[x]++;deg[y]++;
	}
	ll MS=(1<<n);
	for(ll s=MS-2;s>=0;s--){
		G::clear();
		for(ll i=1;i<=n;i++)
			if((s>>i-1)&1)G::a[i][i]=P-1,G::b[i]=P-1;
		for(ll i=1;i<=n;i++){
			if(!((s>>i-1)&1))continue;
			for(ll j=1;j<=n;j++){
				if(!a[i][j])continue;
				if((s|(1<<j-1))==s)
					(G::a[i][j]+=inv[deg[i]])%=P;
				else (G::b[i]+=P-inv[deg[i]]*f[s|(1<<j-1)][j]%P)%=P;
			}
		}
		G::solve(f[s]);
	}
	scanf("%lld",&q);
	while(q--){
		ll m,s=0,x;scanf("%lld",&m);
		for(ll i=1;i<=m;i++)
			scanf("%lld",&x),s|=(1<<x-1);
		scanf("%lld",&x);
		printf("%lld\n",f[(MS-1-s)|(1<<x-1)][x]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/QuantAsk/p/14402759.html