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;
}

浙公网安备 33010602011771号