LOJ2542 随机游走 Min-Max容斥+树上期望DP

搞了一下午 真的是啥都不会
首先这道题要用到Min-Max容斥 得到的结论是

\(Max(S)\)表示集合里最晚被访问的节点被访问的期望步数
\(Min(S)\)表示集合里最早被访问的节点被访问的期望步数
那么$ Max(S) = ∑_{T \in S} {-1^ { \lvert T \rvert+1} }Min(T)$
(这个相关的证明和理解可以看看HDU4336 附一个题解)

考虑对于一个集合\(S\)如何计算\(Min(S)\)
\(d_u\)为点\(u\)的度数
\(u\notin S \space\space \Rightarrow \space \space\displaystyle f_u=f_{fa[u]}+1+\sum (f_{son[u]}+1)\times \frac{1}{d_u}\)
\(u \in S\space\space \Rightarrow \space \space f(u)=0\)
对于树上的期望可以写成$f_u=k_u\times f_{fa[u]}+b_u \(的形式 于是\)\sum f_{son[u]}=\sum_{fa[v]=u}(a_v \times f_u+b_v)$
代入之前的式子并化简得
\(\displaystyle (1-\frac{\sum A_v}{d_u}) f(u) = \frac{1}{d_u}f_{\mathrm{fa}[u]}+(1+\frac{B_v}{d_u})\)
这个\(dfs\)一遍就可以维护所有点的\(a,b\)

考虑如何回答询问
可以对于每个询问的集合\(S\)暴力枚举子集 这样是可以过得
但我们也可以像类似\(FMT\)的做法先维护出所有集合的子集之和再\(O(1)\)回答每个询问 这里注意每个集合初值的正负

#include<bits/stdc++.h>
using namespace std;
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define pa pair<int,int>
#define mod 998244353
#define ll long long
#define mk make_pair
#define pb push_back
#define lb double
#define fi first
#define se second
#define cl(x) memset(x,0,sizeof x)
#ifdef Devil_Gary
#define bug(x) cout<<(#x)<<" "<<(x)<<endl
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define bug(x)
#define debug(...)
#endif
const int INF = 0x7fffffff;
const int N=1e6+5;
const int M=25;
/*
char *TT,*mo,but[(1<<15)+2];
#define getchar() ((TT==mo&&(mo=(TT=but)+fread(but,1,1<<15,stdin),TT==mo))?-1:*TT++)//*/
inline int read(){
    int x=0,rev=0,ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')rev=1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return rev?-x:x;
}
struct Edge{
	int v,nxt;
}e[N<<1];
int n,Q,rt,tot,head[M],bin[N],a[M],b[M],bit[N],d[M],f[N];
void add(int u,int v){
	e[++tot].v=v,e[tot].nxt=head[u],head[u]=tot,++d[u];
	e[++tot].v=u,e[tot].nxt=head[v],head[v]=tot,++d[v];
}
int poww(int x,int y){
	int ans=1;
	while(y){
		if(y&1) ans=(ll)ans*x%mod;
		y>>=1,x=(ll)x*x%mod;
	}
	return ans;
}
void dfs(int x,int fa,int S){
	a[x]=b[x]=0;
	if((1<<x)&S) return;
	for(int i=head[x];i;i=e[i].nxt){
		int j=e[i].v;
		if(j==fa) continue;
		dfs(j,x,S);
		(a[x]+=a[j])%=mod,(b[x]+=b[j])%=mod;
	}
	int tmp=poww((1+mod-(ll)a[x]*d[x]%mod)%mod,mod-2);
	a[x]=(ll)tmp*d[x]%mod,b[x]=(ll)(1+(ll)b[x]*d[x]%mod)*tmp%mod;
//	cout<<x<<" "<<a[x]<<" "<<b[x]<<endl;
}
int main(){
#ifdef Devil_Gary
	freopen("in.txt","r",stdin);
#endif
	n=read(),Q=read(),rt=read()-1;
	for(int i=1;i<n;i++) add(read()-1,read()-1);
	for(int i=0;i<n;i++) d[i]=poww(d[i],mod-2);
    for(int i=1;i<(1<<n);i++) bin[i]=bin[i>>1]+(i&1);
	for(int i=0;i<(1<<n);i++) dfs(rt,-1,i),f[i]=bin[i]&1?b[rt]:(mod-b[rt])%mod;
//	for(int i=0;i<(1<<n);i++) cout<<i<<" "<<f[i]<<endl;
	for(int j=0;j<n;j++) for(int i=0;i<(1<<n);i++)  if(i&(1<<j)) (f[i]+=f[i^(1<<j)])%=mod;
	while(Q--){
		int S=0;
		for(int T=read();T;T--) S|=(1<<(read()-1)); 
//		bug(S);
		printf("%d\n",f[S]);
	}
}

posted @ 2018-06-23 10:59  Devil_Gary  阅读(144)  评论(1编辑  收藏