【CF1540B】Tree Array 题解

题目链接

非常有意思的一道思维题,解法真的让人眼前一亮。

首先,很自然的想法,我们可以枚举根节点,将所有期望求和后除以 $n$ 便可得到答案。

指定完根节点后再来分析题目,题目要求的是逆序对数,我们可以考虑每一对 $(a,b)$ 对答案的贡献。假设 $a > b$,那么我们要求的便是 $a$ 比 $b$ 先取到的概率。因为在取到 $a$ 或 $b$ 前都必须先取到的 $a$ 与 $b$ 的 $LCA$ ,所以我们并不用考虑取到 $LCA$ 前的操作,只需要考虑从 $LCA$ 到 $a$ 和 $b$ 这一段即可。

我们设从 $LCA$ 向 $a$ 或 $b$ 走一步的概率为 $p$ ,那么我们有 $1-2p$ 的概率不做任何操作。但显然的是, $p$的值在每次操作后都会发生变化,并且我们并没有办法去维护它。不过在这题里,我们并不需要去知道走每一步时 $p$ 的具体值,我们只需要保证从 $LCA$ 向 $a$ 与 $b$ 走的概率相同,便可以通过dp计算出先到 $a$ 的概率。具体的,我们设从 $LCA$ 走到 $a$ 要经过 $i$ 步,走到 $b$ 要经过 $j$ 步,那么有 $f_{i,j}=(f_{i-1,j}+f_{i,j-1})/2$. 这也是本题的巧妙之处,树的具体形态不会影响答案,每个数对的贡献只与两个数到他们 $LCA$ 的距离有关

在预处理完 $LCA$ 与 $f$ 数组后,整个算法的时间复杂度为 $O(n^3)$ ,可以通过本题。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL P=1e9+7;
struct Tree{
    int t,nex;
} e[410]; int head[210],tot;
void add(int u,int v) {
    e[++tot].t=v; e[tot].nex=head[u]; head[u]=tot;
}
int n,root;
int fa[210][20],dep[210],lg[210];
void dfs(int x,int fath) {
    dep[x]=dep[fath]+1;
    fa[x][0]=fath;
    for(int i=1;i<=10;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=head[x];i;i=e[i].nex) {
        int y=e[i].t;
        if(y == fath) continue;
        dfs(y,x);
    }
    return;
}
int lca(int x,int y) {
    if(dep[x] < dep[y]) swap(x,y);
    while(dep[x] > dep[y]) x=fa[x][lg[dep[x]-dep[y]]];
    if(x == y) return x;
    for(int i=lg[dep[x]];i>=0;i--) {
        if(fa[x][i] == fa[y][i]) continue;
        x=fa[x][i]; y=fa[y][i];
    }
    return fa[x][0];
}
LL qpow(LL x,LL k) {
    LL sum=1;
    while(k) {
        if(k & 1) sum*=x;
        x*=x;
        x%=P; sum%=P;
        k>>=1;
    }
    return sum;
}
LL ans,dp[210][210];
void solve() {
    memset(fa,0,sizeof(fa));
    memset(dep,0,sizeof(dep));
    dfs(root,0);

    for(int i=1;i<=n;i++) {
        for(int j=1;j<i;j++) {
            int fath=lca(i,j);
            int x=dep[i]-dep[fath],y=dep[j]-dep[fath];
            ans+=dp[x][y];
            ans%=P;
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin>>n;
    for(int i=1,x,y;i<n;i++) {
        cin>>x>>y;
        add(x,y); add(y,x);
    }
    for(int i=1;i<=n;i++) lg[i]=log(i)/log(2);
    for(int i=0;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]=(dp[i-1][j]+dp[i][j-1])*qpow(2,P-2)%P;
    for(int i=1;i<=n;i++) {
        root=i;
        solve();
    }
    cout<<ans*qpow(1ll*n,P-2)%P<<"\n";
    return 0;
}

评论

  1. Akkar1n
    Android Chrome
    11 月前
    2024-2-29 13:30:42

    支持

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇