Luogu P4321 随机漫游
期望DP要倒着推
题意
不一定是树,询问点不一定均为1
$Solution$
设计一个巧妙的DP状态
设$ F(S,x)$表示当前在点$ x$已经走遍了$ S$,走完剩下所有点的期望步数
这样推转移$ DP$的时候一定是从$ F(S|y,y)$转移过来
容易发现$ S|y$->$S$是不可能会变大的,即这维不可能成环
因此从大到小枚举$ S$,对当前$ S$,显然比$ S$大的状态已经被计算,暴力$ n^3$高斯消元消出这维就好了
时间复杂度$ O(2·n·n^3)$
但
为什么不直接$ Min-Max$容斥呢
每次枚举$ S$消出当前$ Min(S,x)$中的x这维
复杂度不变
而且很好写啊...
$ my \ code$
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define p 998244353 #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt,S; bool link[20][20];int a[20][20],d[20]; int inv[20],ans[20][1<<18]; int ksm(int x,int y=p-2){ int ans=1; for(rt i=y;i;i>>=1,x=1ll*x*x%p)if(i&1)ans=1ll*ans*x%p; return ans; } void gauss(){ for(rt i=1;i<=n;i++)if(!(S>>i-1&1)){ for(rt j=i;j<=n;j++)if(a[j][i]){ if(j!=i)for(rt k=1;k<=n+1;k++)swap(a[i][k],a[j][k]); break; } if(!a[i][i])continue; inv[i]=ksm(a[i][i]); for(rt j=i+1;j<=n;j++)if(a[j][i]){ const int x=1ll*a[j][i]*inv[i]%p; for(rt k=i;k<=n+1;k++)if(a[i][k])(a[j][k]-=1ll*a[i][k]*x%p)%=p; } } for(rt i=n;i>=1;i--)if(!(S>>i-1&1)){ ans[i][S]=1ll*a[i][n+1]*inv[i]%p; for(rt j=i-1;j>=1;j--)if(a[j][i])(a[j][n+1]-=1ll*ans[i][S]*a[j][i]%p)%=p; } } int ret[20][1<<18]; int main(){ n=read();m=read(); for(rt i=1;i<=m;i++){ x=read();y=read();d[x]++;d[y]++; link[x][y]=link[y][x]=1; } //Max(S)走遍集合S的时间 //Min(S)第一次走到S的时间 for(S=1;S<(1<<n);S++){ memset(a,0,sizeof(a)); for(rt i=1;i<=n;i++){ if(S>>i-1&1)a[i][i]=1; else { for(rt j=1;j<=n;j++)if(link[i][j]&&!(S>>j-1&1))a[i][j]=-1; a[i][i]=d[i];a[i][n+1]+=d[i]; } } gauss(); } //Max(S)=sigma Min(T) (-1)|T|+1 for(rt i=1;i<=n;i++){ for(rt j=0;j<(1<<n);j++)if(!(__builtin_popcount(j)&1))ans[i][j]*=-1; for(rt j=0;j<n;j++) for(rt k=0;k<(1<<n);k++)if(k>>j&1)(ans[i][k]+=ans[i][k^(1<<j)])%=p; } for(rt T=read();T;T--){ n=read();S=0; for(rt i=1;i<=n;i++)S|=(1<<read()-1); x=read();writeln((ans[x][S]+p)%p); } return 0; }