我暴力过啦
看到这样的东西我们先搬出来\(min-max\)容斥
我们设\(max(S)\)表示\(x\)到达点集\(S\)的期望最晚时间,也就是我们要求的答案了
显然我们也很难求出这个东西,但是我们有\(min-max\)容斥
设\(min(S)\)表示\(x\)第一次到达\(S\)的期望时间,我们就有
\[max(S)=\sum_{T\subseteq S}(-1)^{|T|}min(T)\]
我们现在只需要求出所有\(min(S)\)之后用\(fwt\)做一个子集和就好了
尽管这是一棵树,但是我并没有推出什么优美的转移方程,我们考虑暴力高消
设\(dp_{x,s}\)表示从\(x\)到集合\(s\)的期望步数
显然如果有\(x\in s\),那么\(dp_{x,s}=0\)
否则
\[dp_{x,s}=1+\sum_{(x,v)\in e}\frac{dp_{v,s}}{d_x}\]
于是我们对于每一种\(s\)分别列方程转移就好了
复杂度是\(O(2^{n}n^3)\)
但是我们注意到没有包含\(x\)点的集合只有\(2^{n-1}\)个,同时高消的常数小至\(\frac{1}{8}\),同时很多高消都没有跑满,于是还是跑的挺快的
代码
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define re register #define LL long long inline int read() { char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x; } const int mod=998244353; struct E{int v,nxt;}e[40]; int num,n,Q,X,len; int dp[262145]; int head[19],d[19]; int cnt[262145]; int a[20][20],id[20],vis[20],to[20],inv[20],ans[20]; inline int ksm(int a,int b) { int S=1; while(b) {if(b&1) S=1ll*S*a%mod;b>>=1;a=1ll*a*a%mod;} return S; } inline void add(int x,int y) { e[++num].v=y;e[num].nxt=head[x];head[x]=num; } inline void solve(int S) { int t=0;memset(vis,0,sizeof(vis)); for(re int i=1;i<=n;i++) { if(S&(1<<(i-1))) continue; vis[i]=1;id[++t]=i;to[i]=t; } if(!vis[X]) return; memset(a,0,sizeof(a)); for(re int i=1;i<=t;i++) { int x=id[i]; for(re int j=head[x];j;j=e[j].nxt) if(vis[e[j].v]) a[i][to[e[j].v]]=inv[d[x]]; a[i][i]=mod-1;a[i][t+1]=mod-1; } for(re int i=1;i<=t;i++) { int p=i; for(p=i;p<=t;p++) if(a[p][i]) break; if(p!=i) std::swap(a[i],a[p]); int now=ksm(a[i][i],mod-2); for(re int j=n+1;j>=i;--j) a[i][j]=1ll*a[i][j]*now%mod; for(re int j=i+1;j<=t;j++) for(re int k=t+1;k>=i;--k) { a[j][k]=(a[j][k]-1ll*a[j][i]*a[i][k]%mod); if(a[j][k]<0) a[j][k]=(a[j][k]+mod)%mod; } } ans[t]=a[t][t+1]; for(re int i=t-1;i>=0;--i) { ans[i]=a[i][t+1]; for(re int j=i+1;j<=t;j++) { ans[i]-=1ll*a[i][j]*ans[j]%mod; if(ans[i]<0) ans[i]=(ans[i]+mod)%mod; } } dp[S]=ans[to[X]]; if(cnt[S]&1) return; dp[S]=mod-dp[S]; } inline void Fwt(int *f) { for(re int i=2;i<=len;i<<=1) for(re int ln=i>>1,l=0;l<len;l+=i) for(re int x=l;x<l+ln;++x) { f[x+ln]+=f[x]; if(f[x+ln]>=mod) f[x+ln]%=mod; } } int main() { n=read(),Q=read();X=read(); inv[1]=1; for(re int i=2;i<=n;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod; for(re int x,y,i=1;i<n;i++) { x=read(),y=read(),d[x]++,d[y]++; add(x,y),add(y,x); } len=(1<<n); for(re int i=1;i<len;i++) cnt[i]=cnt[i>>1]+(i&1); for(re int i=1;i<len;i++) solve(i); Fwt(dp); while(Q--) { int k=read(),S=0; for(re int i=1;i<=k;i++) S|=(1<<(read()-1)); printf("%d\n",dp[S]); } return 0; }
我还是来补一下正解吧,据说这是树上随机游走的套路
我们设\(f_x\)表示从\(x\)到点集\(s\)的期望步数
据说树上路径唯一我们可以设\(f_x=A_xf_x+B_x\)
我们写出\(f_x\)的转移
\[f_x=\frac{f_t+\sum_{x\rightarrow c}f_c}{d_x}+1\]
其中\(c\)是\(x\)的儿子
也就是
\[d_xf_x=f_t+\sum_{x\rightarrow c}f_c+d_x\]
\[d_xf_x=f_t+\sum_{x\rightarrow c}A_cf_x+\sum_{x\rightarrow c}B_c+d_x\]
\[(d_x-\sum_{x\rightarrow c}A_c)f_x=f_t+\sum_{x\rightarrow c}B_c+d_x\]
于是我们现在解得
\[A_x=\frac{1}{d_x-\sum_{x\rightarrow c}A_c}\]
\[B_x=\frac{\sum_{x\rightarrow c}B_c+d_x}{d_x-\sum_{x\rightarrow c}A_c}\]
对于在\(S\)集合的点显然满足\(A=B=0\),叶子结点的\(A,B\)我们能直接算,我们一路推到根由于根没有父亲,所以\(f_{rt}=B_{rt}\),这样我们就能把所有的\(f_x\)都算出来了,复杂度是\(O(2^nn)\)
代码就不写了
来源:https://www.cnblogs.com/asuldb/p/10776502.html