P4321 随机漫游
\(n\le18\) 的数据范围显然不是白给的,考虑设计状态中包含一个二进制数 \(S\) 表示走过了哪些关键点。状态设计就是 \(f_{S,u}\),表示已经走过了点集为 \(S\) 的点,现在在 \(u\),走到 \(n\) 的期望步数。
期望 DP 逆推,于是有:
\[f_{S,u}=\frac1{\deg(u)}\Bigg(\sum_{(v,u)\in E}f_{S\cup v,v}\Bigg)+1
\]
这显然是有后效性的,考虑高斯消元。但是 \(2^nn\) 个状态让高斯消元直接似了。发现我们的转移总是由 \(S\) 较大的点转移到 \(S\) 较小的点,这让我们想到从大到小枚举 \(S\),由于比当前的 \(S\) 大的 \(S\) 我们都已经算过了,所以对于每一个 \(S\) 都构造出单独的一个线性方程组,而这个方程组的次数是 \(n\),\(O(n^3)\) 的高斯消元可以承受。然后算出来的值再保存下来即可。
这样复杂度就优化到了 \(O(2^nn^3)\)。
至于询问,由于我们已经算出了每一个状态的答案,那么对于询问集合 \(T\) 和起点 \(x\),我们只需输出 \(f_{(\complement T)\cup x,x}\) 即可,其中 \(\complement 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;
}