CF1540B Tree Array
CF1540B Tree Array
https://codeforces.com/contest/1540/problem/B
一种朴素的想法是求出每个逆序对出现的概率然后累加得到答案。
由于 \(n\) 很小,我们可以考虑枚举每个点为根把树固定住进行计算。
考虑求 \(x,y\) ,\(x\) 在 \(y\) 前面的概率。
我们容易发现,在 \(x,y\) 的 lca 被标记之前,这个树其他部位怎么被标记都是不影响他们之间的概率的。而 \(x,y\) 的 lca 被标记后,我们就得考虑 \(x,y\) 所在的到 lca 的链被标记的情况。我们设 \(f_{a,b}\) 表示 lca 到 \(x\) 距离为 \(a\),到 \(y\) 的距离为 \(b\) 的情况下 \(x\) 的位置在 \(y\) 之前的概率。设随机选到一个点的概率是 \(p\)。那么就有式子:
\[f_{a,b}=p\times f_{a-1,b}+p\times f_{a,b-1}+(1-2p)\times f_{a,b}
\]
由于 \(p\) 是会随着情况变化而变化的,所以看起来这个式子没有卵用,我们试试化简它。
\[\begin{aligned}
f_{a,b}&=p\times f_{a-1,b}+p\times f_{a,b-1}+(1-2p)\times f_{a,b}
\\
2p\times f_{a,b}&=p\times f_{a-1,b}+p\times f_{a,b-1}
\\
f_{a,b}&=\dfrac{1}{2}\times(f_{a-1,b}+f_{a,b-1})
\end{aligned}
\]
我们发现,\(p\) 的值实际上跟 \(f\) 的取值并没有任何关系,所以我们可以直接递推出 \(f\) 的取值。边界就是 \(f_{0,i}=1,f_{0,i}=0\)。
那么我们只要枚举根,再枚举两个节点,然后求出 lca ,求出之间的距离把对应的 \(f\) 值累计上就好了。总时间复杂度 \(O(n^3\times\log n)\)
代码如下:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 205;
const int MOD = 1e9+7;
int n,f[MAXN][21],dep[MAXN],dp[MAXN][MAXN],ans,lg[MAXN];
vector <int> e[MAXN];
ll qpw(ll x,ll b)
{
ll now=1,tmp=x,ans=1;
while(now<=b)
{
if(now&b) ans=tmp*ans%MOD;
tmp=tmp*tmp%MOD;
now<<=1;
}
return ans;
}
void dfs(int p,int fa)
{
dep[p]=dep[fa]+1;f[p][0]=fa;
for(int i=1;i<=lg[dep[p]];++i) f[p][i]=f[f[p][i-1]][i-1];
for(int i=0;i<e[p].size();++i)
{
int to=e[p][i];
if(to==fa) continue;
dfs(to,p);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y]) x=f[x][lg[dep[x]-dep[y]]-1];
if(x==y) return x;
for(int i=lg[dep[x]];i>=0;--i)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
void Solve(int x)
{
memset(f,0,sizeof f);
dfs(x,0);
for(int i=1;i<=n;++i)
{
for(int j=1;j<i;++j)
{
int fa=lca(i,j);
ans=(1ll*ans+1ll*dp[dep[i]-dep[fa]][dep[j]-dep[fa]]*qpw(n,MOD-2)%MOD)%MOD;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
for(int i=1;i<n;++i)
{
int u,v;
scanf("%d %d",&u,&v);
e[u].push_back(v);e[v].push_back(u);
}
for(int i=1;i<=n;++i) dp[0][i]=1,dp[i][0]=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
dp[i][j]=1ll*(dp[i-1][j]+dp[i][j-1])%MOD*qpw(2,MOD-2)%MOD;
for(int i=1;i<=n;++i) Solve(i);
printf("%d\n",ans);
return 0;
}
路漫漫其修远兮,吾将上下而求索。