@ysner
2018-08-13T09:58:13.000000Z
字数 2456
阅读 2879
容斥 DP 期望
给定一棵个结点的树,你从点出发,每次等概率随机选择一条与所在点相邻的边走过去。
有次询问,每次询问给定一个集合,求如果从出发一直随机游走,直到点集中所有点都至少经过一次的话,期望游走几步。
特别地,点(即起点)视为一开始就被经过了一次。
咦??我会状压!
咦?所有点都至少经过一次?求期望?我会容斥!
以上为看题想法
还记得[HNOI2013]游走吧?
在那一道题中,由于转移具有后效性,我们只能用解方程来求出到每个点的概率。
但这里是一颗树!转移没有后效性。
于是可以像一般转移结果。
设表示到达第一次到达号点的期望次数。
先列一个基本的期望转移式:(表示号点的度数,为点的儿子)
枚举所有,按上述式子转移。一旦遇到集合内的数,并。
这样就可得到所有的(在出发点)。
然后容斥就只要用个结论:
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<algorithm>#include<vector>#define re register#define il inline#define ll long long#define max(a,b) ((a)>(b)?(a):(b))#define min(a,b) ((a)<(b)?(a):(b))#define fp(i,a,b) for(re int i=a;i<=b;i++)#define fq(i,a,b) for(re int i=a;i>=b;i--)using namespace std;const int mod=998244353,N=20;struct Edge{int to,nxt;}e[N<<1];int n,Q,rt,h[N],cnt,a[N],k,mx,q[N],d[N];ll A[N],B[N],ans,res[(int)(2e6+100)];il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;++d[u];}il ll gi(){re ll x=0,t=1;re char ch=getchar();while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') t=-1,ch=getchar();while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();return x*t;}il ll ksm(re ll S,re ll o){re ll T=S;S=1;while(o){if(o&1) S=S*T%mod;T=T*T%mod;o>>=1;}return S;}il void dfs(re int u,re int fa,re int S){if((1<<u-1)&S) {A[u]=B[u]=0;return;}re ll a=0,b=0;for(re int i=h[u];i+1;i=e[i].nxt){re int v=e[i].to;if(v==fa) continue;dfs(v,u,S);(a+=A[v])%=mod;(b+=B[v])%=mod;}re ll ysn=ksm((d[u]-a+mod)%mod,mod-2);A[u]=ysn;B[u]=(d[u]+b)*ysn%mod;}il void Min_Max(re int x,re int sz,re int S){if(x>k){if(sz&1) (ans+=res[S])%=mod;else ans=(ans+mod-res[S])%mod;return;}Min_Max(x+1,sz,S);Min_Max(x+1,sz+1,S|(1<<q[x]-1));}int main(){memset(h,-1,sizeof(h));n=gi();Q=gi();rt=gi();mx=(1<<n)-1;fp(i,1,n-1){re int u=gi(),v=gi();add(u,v);add(v,u);}fp(S,1,mx){fp(i,1,n) A[i]=B[i]=0;dfs(rt,0,S);res[S]=B[rt];}while(Q--){k=gi();ans=0;fp(i,1,k) q[i]=gi();Min_Max(1,0,0);printf("%lld\n",ans);}return 0;}
