zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Selenium2Library+ride学习笔记
    windbg 调试技巧
    LINUX常用命令--重定向、管道篇(四)
    Linux文件系统与结构
    windbg命令学习4
    windbg命令学习3
    windbg命令学习2
    MySQL常用操作命令
    Httpwatch 工具介绍
    windows平台上用python 远程线程注入,执行shellcode
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14402759.html
Copyright © 2011-2022 走看看