luogu P4321 随机漫游 期望dp 二进制 高斯消元

LINK:随机漫游

非常妙的一道题。

容易想到倒推期望。

设状态 f[i][j]表示到达第i个点 此时已经到达的集合为j能走到全集的期望边数。

只要求出来这个就能O(1)回答询问。

\(f[i][j]=1+\sum_{v\in son_x,v\notin j}\frac{1}{d_i}f[i][j|v]+\sum_{v\in son_x,v\in j}\frac{1}{d_i}f[i][j]\)

有了这个东西 显然可以\((2^n\cdot n)^3\)暴力高斯消元了。

考虑优化 容易发现如果按照二进制的大小倒着推这个东西 那么前面那个部分完全已知了。

这样我们每次的方程最多n个 总复杂度\(2^n\cdot n^3\)

需要卡卡常数 如 消的时候d==0可以直接break了。

const ll MAXN=19;
ll f[MAXN][1<<MAXN];//f[i][j]表示到达i这个点此时集合为j还需要跑多少才能得到全集的期望边数.
ll n,m,Q;
ll a[MAXN][MAXN],d[MAXN];
ll b[MAXN][MAXN],g[MAXN];
inline ll ksm(ll b,ll p){ll cnt=1;while(p){if(p&1)cnt=(ll)cnt*b%mod;b=(ll)b*b%mod;p=p>>1;}return cnt;}
inline void GAUSS()
{
	rep(1,n,i)
	{
		ll p=i;
		rep(i,n,j)if(b[j][i]){p=j;break;}
		if(p!=i){rep(1,n,j)swap(b[i][j],b[p][j]);swap(g[i],g[p]);}
		ll ww=ksm(b[i][i],mod-2);
		rep(1,n,j)
		{
			if(i==j)continue;
			ll d=ww*b[j][i]%mod;
			if(!d)continue;
			rep(1,n,k)b[j][k]=(b[j][k]-b[i][k]*d)%mod;
			g[j]=(g[j]-g[i]*d)%mod;
		}
	}
	rep(1,n,i)g[i]=g[i]*ksm(b[i][i],mod-2)%mod;
}
signed main()
{
	freopen("1.in","r",stdin);
	get(n);get(m);
	rep(1,m,i)
	{
		ll get(x),get(y);
		a[x][y]=a[y][x]=1;
		++d[x];++d[y];
	}
	rep(1,n,i)d[i]=ksm(d[i],mod-2);
	ll maxx=(1<<n)-1;
	fep(maxx-1,1,j)
	{
		//求出每个f[i][j].
		rep(1,n,i)
		{
			b[i][i]=1;g[i]=0;
			if(j&(1<<(i-1)))
			{
				++g[i];
				rep(1,n,k)
				{
					if(a[i][k])
					{
						if(!(j&(1<<(k-1))))g[i]=(g[i]+d[i]*f[k][j|(1<<(k-1))])%mod;
						else b[i][k]=(b[i][k]-d[i])%mod;
					}
				}
			}
		}
		GAUSS();
		rep(1,n,i)f[i][j]=(g[i]+mod)%mod;
	}
	get(Q);
	rep(1,Q,i)
	{
		ll st,s=0,get(x);
		rep(1,x,i)
		{
			ll get(y);
			s=s|(1<<(y-1));
		}
		get(st);
		putl(f[st][(maxx^s)|(1<<(st-1))]);
	}
	return 0;
}
posted @ 2020-04-23 22:16  chdy  阅读(199)  评论(0编辑  收藏  举报