P5643 [PKUWC2018] 随机游走

题目要求求出点集 \(S\) 中所有点经过一遍的期望步数。
这显然不好处理,考虑用 \(\min-\max\) 容斥进行转化。

转化:min-max 容斥

\(\min-\max\) 容斥,有:

\[E(\max_{i \in S} a_i) = \sum_{T \subseteq S} (-1)^{\left| T \right|+1} E(\min_{i \in T} a_i) \]

于是问题可以转化为求一个点到达点集 \(S\) 中任意一个点的期望步数,记为 \(f_u\)
对于一个点 \(u\) 进行分析;
\(u \in S\),那么 \(f_u =0\);
否则有以下式子:

\[f_u = \dfrac{1}{deg_u}(f_{fa_u} + \sum_{v \in son}f_v) + 1 \]

trick:待定系数法

\(f_u = k_u f_{fa_u} + b_u\).
那么原式变为:

\[f_u = \dfrac{1}{deg_u}(f_{fa_u} + \sum_{v \in son}(k_v f_u + b_v)) +1 \]

\[deg_u(f_u-1) = f_{fa_u} + \sum_{v \in son}(k_v f_u + b_v) \]

\[deg_uf_u-deg_u = f_{fa_u} + f_u\sum_{v\in son}k_v + \sum_{v\in son} b_v \]

\[f_u(deg_u - sum_k) = f_{fa_u} + sum_b + deg_u \]

\[f_u = f_{fa_u} \frac{1}{deg_u -sum_k} + \frac{deg_u+sum_b}{deg_u-sum_k} \]

trick:高维前缀和

考虑暴力处理所有选定子集,时间复杂度为 \(\mathcal{O(n 2^n)}\)
然后考虑用 高维前缀和 求容斥时的所有子集之和。

#include<bits/stdc++.h>
#define ll long long

using namespace std;

const int N = 20,mod = 998244353;

struct node{
	int to,nxt;
}e[N<<1];
int tot,head[N];
int n,Q,x;
int deg[N];
int In[N],K[N],B[N];
ll f[1<<N];

void add_Edge(int u,int v){
	e[tot].to=v,e[tot].nxt=head[u];
	head[u]=tot++;
}

ll qpow(ll a,ll b){
	ll res=1;
	while(b){
		if(b&1)res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}
void dfs(int u,int fa) {
	if(In[u]){
		K[u]=0,B[u]=0;
		return ;
	}
	
	ll sumb,sumk;
	sumb=sumk=0;
	for(int i=head[u];~i;i=e[i].nxt) {
		int v=e[i].to;
		if(v==fa)continue;
		dfs(v,u);
		(sumb+=B[v])%=mod,(sumk+=K[v])%=mod;
	}
	
	ll Inv=qpow((deg[u]-sumk+mod)%mod,mod-2);
	K[u]=Inv;
	B[u]=1ll*(deg[u]+sumb)%mod*Inv%mod;
}
void Init() {
	for(int i=1;i<(1<<n);i++){
		for(int j=1;j<=n;j++)
			In[j]=(i>>j-1)&1;
		dfs(x,0);
		f[i]=B[x];
	}
	
	for(int i=1;i<(1<<n);i++){
		int t=__builtin_popcount(i)&1;
		if(!t)f[i]=(mod-f[i])%mod;
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<(1<<n);j++){
			if(j&(1<<i-1))f[j]=(f[j]+f[j^(1<<i-1)])%mod;
		}
	}
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	memset(head,-1,sizeof(head));
	cin>>n>>Q>>x;
	
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		add_Edge(u,v);
		add_Edge(v,u);
		deg[u]++,deg[v]++;
	}
	
	Init();
	
	while(Q--){
		int m;
		cin>>m;
		int cur=0;
		for(int i=1;i<=m;i++){
			int t;
			cin>>t;
			cur|=1<<(t-1);
		}
		cout<<f[cur]<<"\n";
	}
	return 0;
}

posted @ 2026-01-17 10:43  Harvey-zhuhy  阅读(1)  评论(0)    收藏  举报