正题
题目链接:https://www.luogu.com.cn/problem/P4321
题目大意
给出nnn个点mmm条边的一张无向图,qqq次询问。
每次询问给出一个点集和一个起点,求从起点出发随机游走经过所有点集的期望步数。
n∈[1,18],m∈[1,n(n−1)2],q∈[1,105]n\in[1,18],m\in[1,\frac{n(n-1)}{2}],q\in[1,10^5]n∈[1,18],m∈[1,2n(n−1)],q∈[1,105]
解题思路
首先nnn很小可以状压经过点的状态,然后因为这个询问是给出起始状态所以需要倒推。设fs,xf_{s,x}fs,x表示目前状态是sss,在点xxx,覆盖所有点的期望次数。
那么有方程
fS,x=∑x−>yfS∩y,yf_{S,x}=\sum_{x->y}f_{S\cap y,y}fS,x=x−>y∑fS∩y,y
然后SSS不同的当常数,相同的高斯消元转移即可。
时间复杂度O(2nn3)O(2^nn^3)O(2nn3)
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;
}