题目大意:
给定一棵树,求出一对起点和终点,使得从起点随机游走,到终点停下的期望步数最多,输出这个期望步数
solution:
其实不难
树形 \(dp\)
首先,另 \(f[x]\) 表示从 \(x\) 点走到他父亲的期望步数
它有可能先走到某个儿子里,然后再回来,也有可能直接走到它的父亲
因此:
\[f[x]=\frac{1}{|son[x]|+1}+\sum_{u ∈ son[x]}\frac{f[u]+f[x]+1}{|son[x]|+1} \]有:
\[f[x]=\frac{1}{|son[x]|+1}+f[x]*\sum_{u ∈ son[x]}\frac{1}{|son[x]|+1}+\sum_{u ∈ son[x]}\frac{f[u]+1}{|son[x]|+1} \] \[f[x]=\frac{1}{|son[x]|+1}+f[x]*\frac{|son[x]|}{|son[x]|+1}+\sum_{u ∈ son[x]}\frac{f[u]+1}{|son[x]|+1} \] \[\frac{f[x]}{|son[x]|+1}=\frac{1}{|son[x]|+1}+\sum_{u ∈ son[x]}\frac{f[u]+1}{|son[x]|+1} \]最后得到:
\[f[x]=|siz[x]|+1+\sum_{u ∈ son[x]}f[u] \]也就是说你把它子树内所有点的度数加起来就可以了
然后,另 \(g[x]\) 表示从 \(x\) 的父亲走到 \(x\) 的期望步数
从它父亲出发,有可能先走到其它的某个子树中再回来,也有可能走到它父亲的父亲再回来,也有可能直接走到 \(x\)
因此:
\[g[x]=\frac{g[x]+g[fa[x]]+1}{|son[x]|+1}+\frac{1}{|son[x]|+1}+\sum_{u ∈ son[fa[x]],u!=x}\frac{g[x]+f[u]+1}{|son[x]|+1} \]有:
\[g[x]=g[x]*\frac{|son[x]|}{|son[x]|+1}+\frac{g[fa[x]]+1}{|son[x]|+1}+\frac{1}{|son[x]|+1}+\sum_{u ∈ son[fa[x]],u!=x}\frac{f[u]+1}{|son[x]|+1} \] \[\frac{g[x]}{|son[x]|+1}=\frac{g[fa[x]]+1}{|son[x]|+1}+\frac{1}{|son[x]|+1}+\sum_{u ∈ son[fa[x]],u!=x}\frac{f[u]+1}{|son[x]|+1} \] \[g[x]=(g[fa[x]]+1)+1+\sum_{u ∈ son[fa[x]],u!=x}(f[u]+1) \] \[g[x]=(g[fa[x]]+1)+1+(\sum_{u ∈ son[fa[x]],u!=x}f[u])+(|son[x]|-1) \] \[g[x]=g[fa[x]]+(|son[x]|+1+\sum_{u ∈ son[fa[x]],u!=x}f[u]) \]最后得到:
\[g[x]=g[fa[x]]+f[fa[x]]-f[x] \]都很简洁,两次树形 \(dp\) 就可以求出
知道了每条边向上走和向下走的期望步数,第三次树形 \(dp\) 求出树的带权直径即可
#include <bits/stdc++.h>
using namespace std;
int n;
int ver[200005],ne[200005],head[200005],cnt;
inline void link(int x,int y){
ver[++cnt]=y;
ne[cnt]=head[x];
head[x]=cnt;
}
int siz[100005];
int f[100005],g[100005];
void dfs1(int x,int fi){
f[x]=siz[x];
for(int i=head[x];i;i=ne[i]){
int u=ver[i];
if(u==fi)continue;
dfs1(u,x);
f[x]+=f[u];
}
}
void dfs2(int x,int fi){
for(int i=head[x];i;i=ne[i]){
int u=ver[i];
if(u==fi)continue;
g[u]=f[x]-f[u]+g[x];
dfs2(u,x);
}
}
int dp[2][100005],ans;
void dfs3(int x,int fi){
for(int i=head[x];i;i=ne[i]){
int u=ver[i];
if(u==fi)continue;
dfs3(u,x);
ans=max(ans,dp[0][x]+dp[1][u]+f[u]);
ans=max(ans,dp[1][x]+dp[0][u]+g[u]);
dp[0][x]=max(dp[0][x],dp[0][u]+g[u]);
dp[1][x]=max(dp[1][x],dp[1][u]+f[u]);
}
}
int main(){
freopen("rw.in","r",stdin);
freopen("rw.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
link(x,y);link(y,x);
siz[x]++;siz[y]++;
}
dfs1(1,1);
dfs2(1,1);
dfs3(1,1);
printf("%d.00000",ans);
return 0;
}
[PKUWC2018]随机游走
首先用 \(min-max\) 容斥将题目转化为求从根到达集合内任意一点的期望步数
\[\begin{cases} dp[x,S]=(dp[fa,S]+1)*\frac {1}{deg[x]}+\sum_{u\in son[x]}(dp[u,S]+1)*\frac {1}{deg[x]} \ (x \notin S)\\ dp[x,S]=0 \ (x \in S)\\ \end{cases} \]只考虑 \(x\notin S\) 的情况:
设:
\[dp[x]=A_x*dp[fa]+B_x \]带入原式中:
\[dp[x]=(dp[fa]+1)*\frac {1}{deg[x]}+\sum_{u\in son[x]}(dp[u]+1)*\frac {1}{deg[x]+1} \] \[dp[x]=\frac {1}{deg[x]}*(dp[fa]+\sum_{u\in son[x]}dp[u])+1 \] \[dp[x]=\frac {1}{deg[x]}*(dp[fa]+\sum_{u\in son[x]}A_u*dp[x]+\sum_{u\in son[x]} B_u)+1 \] \[dp[x]*deg[x]=dp[fa]+\sum_{u\in son[x]}A_u*dp[x]+\sum_{u\in son[x]} B_u+deg[x] \] \[(deg[x]-\sum_{u\in son[x]}A_u)*dp[x]=dp[fa]+\sum_{u\in son[x]} B_u+deg[x] \] \[dp[x]=\frac {1}{deg[x]-\sum_{u\in son[x]}}*dp[fa]+\frac{\sum_{u\in son[x]} B_u+deg[x]}{deg[x]-\sum_{u\in son[x]}} \]因此,解得:
\[A=\frac {1}{deg[x]-\sum_{u\in son[x]}} \] \[B=\frac{\sum_{u\in son[x]} B_u+deg[x]}{deg[x]-\sum_{u\in son[x]}} \]由于根没有父亲,那么对于集合 \(S\),有:
\[ans[S]=dp[rt,S]=B_{rt} \]对每个集合进行一次树形 \(dp\) ,再来一次 \(fwt\) 即可求出所有集合的答案
#include<bits/stdc++.h>
using namespace std;
int n,q,rt;
int ver[45],ne[45],head[45],tot,deg[45];
inline void link(int x,int y){
ver[++tot]=y;
ne[tot]=head[x];
head[x]=tot;deg[y]++;
}
long long a[21],b[21];
const long long md=998244353;
inline long long pwr(long long x,long long y){
long long res=1;
while(y){
if(y&1)res=res*x%md;
x=x*x%md;y>>=1;
}return res;
}
void dfs(int x,int fi,int S){
if((S>>(x-1))&1)return ;
long long tota=0,totb=0;
for(int i=head[x];i;i=ne[i]){
int u=ver[i];
if(u==fi)continue;
dfs(u,x,S);
tota=(tota+a[u])%md;totb=(totb+b[u])%md;
}
a[x]=pwr(deg[x]-tota,md-2);
b[x]=(deg[x]+totb)%md*a[x]%md;
}
long long dp[1<<18];
int cnt[1<<18];
int main(){
scanf("%d%d%d",&n,&q,&rt);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
link(x,y);link(y,x);
}//puts("111");
for(int s=1;s<(1<<n);s++){
cnt[s]=cnt[s>>1]+(s&1);
for(int i=1;i<=n;i++)a[i]=b[i]=0;
dfs(rt,rt,s);dp[s]=(cnt[s]&1?1:-1)*b[rt];
}//puts("222");
for(int i=0;i<n;i++){
for(int s=0;s<(1<<n);s++){
if((s>>i)&1)continue;
dp[s|(1<<i)]=(dp[s|(1<<i)]+dp[s])%md;
}
}
while(q--){
int k,s=0;
scanf("%d",&k);
while(k--){
int x;scanf("%d",&x);
s|=(1<<(x-1));
}printf("%lld\n",(dp[s]+md)%md);
}
return 0;
}