树上随机游走与树上高斯消元
题目大意:
给定一棵树,求出一对起点和终点,使得从起点随机游走,到终点停下的期望步数最多,输出这个期望步数
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;
}
SCZ 2022-02-24 山泽麟迹
有一个数字,初始值为 \(0\),有 \(m\) 个各不相同的数 \(a_i\) ,每次有 \(1-p\) 的概率从比不大于上次加的数中等概率选择一个数给你的数字加上这个数,另有 \(p\) 的概率退回到上次操作前的状态,问期望多少次操作后你手中的数字不小于 \(n\) 。
数据范围:\(n,m,a_i \le 50\) 。
先不考虑回退的操作,枚举所有方案,发现总方案数并不多,考虑建出搜索树,问题转化为从根节点走到叶子的期望步数,对于点 \(x\) ,有:
\[dp[x]=p\times dp[fa]+(1-p)\times\sum_{u\in son[x]}\frac{dp[u]}{|son[x]|}+1
\]
套路地设:
\[dp[x]=A_x\times dp[fa]+B_x
\]
有:
\[dp[x]=p\times dp[fa]+(1-p)\times\sum_{u\in son[x]}\frac{A_u\times dp[x]+B_u}{|son[x]|}+1\\
\\
dp[x]=p\times dp[fa]+(1-p)\times(\sum_{u\in son[x]}\frac{A_u}{|son[x]|}\times dp[x]+\sum_{u\in son[x]}\limits\frac{B_u}{|son[x]|})+1\\
\\
(1-(1-p)\times\sum_{u\in son[x]}\frac{A_u}{|son[x]|})\times dp[x]=p\times dp[fa]+(1-p)\times\sum_{u\in son[x]}\frac{B_u}{|son[x]|}+1\\
\\
(\frac{|son[x]|-(1-p)\times\sum_{u\in son[x]}\limits A_u}{|son[x]|})\times dp[x]=p\times dp[fa]+\frac{(1-p)\times\sum_{u\in son[x]}\limits B_u+|son[x]|}{|son[x]|}\\
\]
得到:
\[dp[x]=\frac{p\times|son[x]|}{|son[x]|-(1-p)\times\sum_{u\in son[x]}\limits A_u} \times dp[fa]+\frac{(1-p)\times\sum_{u\in son[x]}\limits B_u+|son[x]|}{|son[x]|-(1-p)\times\sum_{u\in son[x]}\limits A_u}\\
\]
因此:
\[A_x=\frac{p\times|son[x]|}{|son[x]|-(1-p)\times\sum_{u\in son[x]}\limits A_u}
\]
\[B_x=\frac{(1-p)\times\sum_{u\in son[x]}\limits B_u+|son[x]|}{|son[x]|-(1-p)\times\sum_{u\in son[x]}\limits A_u}
\]
对于根节点 \(p=0\) ,最终答案为:
\[\frac{\sum_{u\in son[1]}\limits B_u+|son[1]|}{|son[1]|-\sum_{u\in son[1]}\limits A_u}
\]
#include<bits/stdc++.h>
using namespace std;
double k[2000005],b[2000005],P;
int cnt,t,n,a[55];
void dfs(int sum,int lim,int x){
if(sum>=t)return k[x]=b[x]=0,void();
double K=0,B=0;
for(int i=lim;i;i--){
int u=++cnt;
dfs(sum+a[i],i,u);
K+=k[u];B+=b[u];
}
if(x>1)k[x]=lim*P/(lim-(1-P)*K),b[x]=((1-P)*B+lim)/(lim-(1-P)*K);
else printf("%.3lf\n",(B+n)/(n-K));
}
int main(){
int i,j;
scanf("%lf%d%d",&P,&t,&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+n+1),dfs(0,n,++cnt);
return 0;
}