P4321 随机漫游 题解

P4321 随机漫游

n18 的数据范围显然不是白给的,考虑设计状态中包含一个二进制数 S 表示走过了哪些关键点。状态设计就是 fS,u,表示已经走过了点集为 S 的点,现在在 u,走到 n 的期望步数。

期望 DP 逆推,于是有:

fS,u=1deg(u)((v,u)EfSv,v)+1

这显然是有后效性的,考虑高斯消元。但是 2nn 个状态让高斯消元直接似了。发现我们的转移总是由 S 较大的点转移到 S 较小的点,这让我们想到从大到小枚举 S,由于比当前的 S 大的 S 我们都已经算过了,所以对于每一个 S 都构造出单独的一个线性方程组,而这个方程组的次数是 nO(n3) 的高斯消元可以承受。然后算出来的值再保存下来即可。

这样复杂度就优化到了 O(2nn3)

至于询问,由于我们已经算出了每一个状态的答案,那么对于询问集合 T 和起点 x,我们只需输出 f(T)x,x 即可,其中 T 是 T 关于全集的补集。这样回答询问就是 O(1) 的。

#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
#define inv(x) power(x,MOD-2)
using namespace std;

char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x;
}
template<typename T>
void write(T x,char sf='\n'){
	if(x<0)putchar('-'),x=~x+1;
	int top=0;
	do str[top++]=x%10,x/=10;while(x);
	while(top)putchar(str[--top]+48);
	if(sf^'#')putchar(sf);
}
using ll=long long;
constexpr int MAXN=20,MAXM=405;
constexpr ll MOD=998244353;
int n,m,q,deg[MAXN];
vector<int>g[MAXN];
ll a[MAXN][MAXN],f[1<<18][MAXN];

ll power(ll a,ll b){
	ll res=1;
	for(;b;a=a*a%MOD,b>>=1)if(b&1)res=res*a%MOD;
	return res;
}
void add(ll&x,ll y){
	x=(x+y>=MOD?x+y-MOD:x+y);
}
void sub(ll&x,ll y){
	x=(x-y<0?x-y+MOD:x-y);
}
void gauss_jordan(){
	for(int i=1;i<=n;i++){
		for(int k=i;k<=n;k++)
			if(a[k][i]){
				swap(a[i],a[k]);
				break;
			}
		for(int k=1;k<=n;k++){
			if(!a[k][i]||k==i) continue;
			ll t=a[k][i]*inv(a[i][i])%MOD;
			for(int j=i;j<=n+1;j++)
				sub(a[k][j],t*a[i][j]%MOD);
		}
	}
	for(int i=1;i<=n;i++) a[i][n+1]=a[i][n+1]*inv(a[i][i])%MOD;
}

int main(){
	n=read(),m=read();
	for(int i=1,u,v;i<=m;i++){
		u=read(),v=read();
		g[u].emplace_back(v);
		g[v].emplace_back(u);
		deg[u]++,deg[v]++;
	}
	int B=(1<<n)-1;
	for(int s=B-1;s;s--){
		memset(a,0,sizeof(a));
		for(int i=1;i<=n;i++){
			if(!(s&1<<(i-1))) continue;
			a[i][i]=1,a[i][n+1]=1;
			ll fk=inv(deg[i]);
			for(auto v:g[i])
				if(s&1<<(v-1)) a[i][v]=MOD-fk;
				else add(a[i][n+1],fk*f[s|1<<(v-1)][v]%MOD);
		}
		gauss_jordan();
		for(int i=1;i<=n;i++)
			if(s&1<<(i-1))
				f[s][i]=a[i][n+1]; 
	}
	q=read();
	while(q--){
		int k=read(),s=0;
		for(int i=1;i<=k;i++) s|=1<<(read()-1);
		int x=read();
		write(f[(B^s)|1<<(x-1)][x]);
	}
	return fw,0;
}
posted @   Laoshan_PLUS  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示