菜鸡选手省选将近科技都没有点全。。又被xgcxgcD飞了
首先直接minmax容斥,max就是S最后一次被访问的期望,min就是S第一次被访问的方案
枚举每个集合,设f[x]为x出发min(S)的期望,若x属于S则f[x]=0,否则f[x]=(sigema f[son] + f[fa])/deg[x] +1
直接高斯会爆炸,但是我们可以把每个点用父亲表示出来,推完就是这样的。f[x]=f[fa]/(deg[x]+Asum)+(deg[x]+Bsum)/(deg[x]+Asum)
A[x]=1/(deg[x]+Asum),B[x]=(deg[x]+Bsum)/(deg[x]+Asum)
min(S)=B[rt]
然后子集卷积一下把答案预处理出来就行了
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; const int _=1e2; const int maxn=18+2; const int fbin=(1<<18)+_; const int mod=998244353; inline int ad(int x,int y){return (x>=mod-y)?(x-mod+y):x+y;} inline int re(int x,int y){return (x<y)?(x-y+mod):x-y;} inline int mu(int x,int y){return (LL)x*y%mod;} inline int qp(int x,int y){int r=1;while(y!=0){if(y&1)r=mu(r,x);x=mu(x,x);y/=2;}return r;} struct node { int x,y,next; }a[2*maxn];int len,last[maxn]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int S,mx[fbin],A[maxn],B[maxn];//f(x)=A[x]*f(fa)+B[x] void dfs(int x,int fr) { if(S&(1<<x-1)){A[x]=0,B[x]=0;return ;} for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fr)dfs(y,x); } int deg=(fr!=0),Asum=0,Bsum=0; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fr) { deg++; Asum=ad(Asum,A[y]); Bsum=ad(Bsum,B[y]); } } A[x]=qp(re(deg,Asum),mod-2); B[x]=mu(ad(deg,Bsum),A[x]); } int o[fbin]; int main() { int n,li,Q,rt,x,y; scanf("%d%d%d",&n,&Q,&rt); li=(1<<n); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); ins(x,y),ins(y,x); } int mn; for(S=0;S<li;S++) { dfs(rt,0),mn=B[rt]; o[S]=o[S>>1]+(S&1); mx[S]=(o[S]&1)?mn:re(0,mn); } for(int i=1;i<=n;i++) for(int zt=0;zt<li;zt++) if(zt&(1<<i-1))mx[zt]=ad(mx[zt],mx[zt^(1<<i-1)]); int m,zt; while(Q--) { scanf("%d",&m); zt=0; for(int i=1;i<=m;i++) { scanf("%d",&x); zt|=(1<<x-1); } printf("%d ",mx[zt]); } return 0; }