LG P5643 [PKUWC2018]随机游走
Description
给定一棵 $n$个结点的树,你从点 $x$出发,每次等概率随机选择一条与所在点相邻的边走过去。
有 $q$次询问,每次询问给定一个集合$S$,求如果从 $x$出发一直随机游走,直到点集$S$ 中所有点都至少经过一次的话,期望游走几步。
特别地,点 $x$(即起点)视为一开始就被经过了一次。
答案对$998244353$ 取模。
Solution
把遍历整个点集的期望时间视为遍历点集中每个点的时间的最大值,就可以使用MinMax容斥
$$Max(S)=\sum_{T \subseteq S}(-1)^{|T|-1}Min(T)$$
令$f(i,S)$表示从$i$到达点集$S$的点的时间最小值,即访问任意一个点的期望时间,所以$Min(S)=f(i,S)$
其表达式为
$$f(i,S)=
\begin{cases}
0& \text{$i \in S$}\\
\frac{f(fa_i,S)}{deg_i} + \frac{\sum_{j \in son_i}f(j,S)}{deg_i}+1& \text{$i \notin S$}\\
\end{cases}$$
设$f(i,S)=A_i \times f(fa_i,S)+B_i$,代入化简得
$$A_i=\frac{1}{deg_i - \sum_{j \in son_i}A_j},B_i=\frac{\sum_{j\in son_i} B_j + deg_i}{deg_i - \sum_{j \in son_i}A_j}$$
预处理子集答案,从下向上递推
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> using namespace std; long long n,q,root,tot,head[20],A[20],B[20],deg[20],bits[1100000],ans[1100000]; const long long mod=998244353; struct Edge { long long to,nxt; } edge[40]; inline long long read() { long long f=1,w=0; char ch=0; while(ch<'0'||ch>'9') { if(ch=='-') { f=-1; } ch=getchar(); } while(ch>='0'&&ch<='9') { w=(w<<1)+(w<<3)+ch-'0'; ch=getchar(); } return f*w; } long long ksm(long long a,long long p) { long long ret=1; while(p) { if(p&1) { (ret*=a)%=mod; } (a*=a)%=mod; p>>=1; } return ret; } void dfs(long long k,long long fa,long long sta) { if((1<<(k-1))&sta) { A[k]=B[k]=0; return; } A[k]=B[k]=deg[k]; for(long long i=head[k]; i; i=edge[i].nxt) { long long v=edge[i].to; if(v!=fa) { dfs(v,k,sta); ((A[k]-=A[v])+=mod)%=mod; (B[k]+=B[v])%=mod; } } A[k]=ksm(A[k],mod-2); (B[k]*=A[k])%=mod; } int main() { n=read(); q=read(); root=read(); for(long long i=1; i<n; i++) { long long u=read(),v=read(); edge[++tot]=(Edge){v,head[u]}; head[u]=tot; edge[++tot]=(Edge){u,head[v]}; head[v]=tot; ++deg[u]; ++deg[v]; } for(long long i=1;i<(1<<n);i++) { bits[i]+=bits[i>>1]+(i&1); } for(long long i=1; i<(1<<n); i++) { dfs(root,0,i); if(bits[i]&1) { ans[i]=B[root]; } else { ans[i]=mod-B[root]; } } for(long long i=0;i<n;i++) { for(long long j=0;j<(1<<n);j++) { if((1<<i)&j) { (ans[j]+=ans[j^(1<<i)])%=mod; } } } for(long long i=1;i<=q;i++) { long long k=read(),nowsta=0; for(long long j=1;j<=k;j++) { long long num=read(); nowsta|=(1<<(num-1)); } printf("%lld\n",ans[nowsta]); } return 0; }