期望dp ---- B. Tree Array 思维+期望dp 逆序对期望数

题目大意


题目大意:

n ( n ≤ 200 ) n(n\leq200) n(n200)个节点的树。初始的时候,等概率随机选择一个点标记,接来随机选择一个与标记点相连的未标记点来标记,直到所有的点都被标记。根据点被标记的顺序,生成一个数列。这个数列的逆序对的期望个数为多少个?


解题思路:

  1. 我们先可以对每一个逆序对进行考虑?
  2. 比如我们考虑 a ≤ b a\leq b ab就是 a a a出现在 b b b后面的概率 P b a P_{ba} Pba是多少?
  3. 我们先固定一个节点为根去考虑,设 L C A LCA LCA a a a b b b的最近公共祖先,那么概率 P b a P_{ba} Pba只和 a , b a,b a,b简单路径上面的点有关 !!
  4. 为什么呢?
  5. 对于固定的根节点,我们每次都会得到一个点集的排列,但是我们发现,除了 a a a b b b路径上的点之外,其他的点可以出现在任意的合理的排列,那么我们实际上是不是只考虑 a , b a,b a,b路径上的点就好了
  6. 现在设 x = d i s t ( L C A , a ) , y = d i s t ( L C A , b ) x=dist(LCA,a),y=dist(LCA,b) x=dist(LCA,a),y=dist(LCA,b)那么就相当于从里面两个桶分别有 x , y x,y x,y个球,去拿球,且 b b b最先被拿到的概率
  7. 那么就有 d p [ i ] [ j ] dp[i][j] dp[i][j]为第一个桶有 i i i个求,第二个桶有 j j j个求,且第二个桶先被拿完的概率。
  8. 转移计算 d p [ i ] [ j ] = 1 2 ( d p [ i − 1 ] [ j ] × d p [ i ] [ j − 1 ] ) dp[i][j]=\frac{1}{2}(dp[i-1][j]\times dp[i][j-1]) dp[i][j]=21(dp[i1][j]×dp[i][j1])
  9. 那么思路就很明了了,就是枚举根节点,计算 L C A LCA LCA枚举点对,去计算每个逆序对出现的概率,累加就好了
  10. 我们可以先预处理出 d p dp dp数组
  11. L C A LCA LCA倍增算就可以了

AC code

O ( n 3 l o g n ) O(n^3logn) O(n3logn)

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 7;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}
ll dp[202][202], ans;
int n, fa[202][21], depth[202];
vector<int> G[202];
ll qim(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % mod;
        b >>= 1;
        a = a * a % mod; 
    }
    return res % mod;
}

inline void dfs(int u, int f) {
    fa[u][0] = f;
    depth[u] = depth[f]+1;
    for(int i = 1; i < 20; ++ i)
      fa[u][i] = fa[fa[u][i-1]][i-1];
    for(auto it : G[u]) {
        if(it == f) continue;
        dfs(it,u);
    } 
}

inline int lca(int u, int v) {
    if(depth[u] < depth[v]) swap(u,v);
    int delta = depth[u] - depth[v];
    for(int i = 19; i >= 0; -- i)
      if(delta >> i & 1) 
        u = fa[u][i];
    if(u == v) return v;
    for(int i = 19; i >= 0; -- i)
       if(fa[u][i] != fa[v][i])
         u = fa[u][i], v = fa[v][i];
    return fa[u][0];
}

inline void slove(int root) {
    ms(fa,0);
    ms(depth,0);
    dfs(root,0);
    for(int i = 1; i <= n; ++ i)// 枚举点对
       for(int j = 1; j <= i - 1; ++ j) {
            int LCA = lca(i, j);
            ans = (ans + dp[depth[i] - depth[LCA]][depth[j] - depth[LCA]]) % mod;
       }
}

inline void init() {
    for(int i = 0; i <= n; ++ i) dp[0][i] = 1; // 边界条件,我这里写的和上面的x,y反了过来,第一维是大数
    ll inv2 = qim(2,mod-2);
    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])*inv2%mod;
}

int main() {
    IOS;
    cin >> n;
    for(int i = 1; i < n; ++ i) {
        int u, v;
        cin >> u >> v;
        G[v].push_back(u);
        G[u].push_back(v);
    }
    init();
    for(int i = 1; i <= n; ++ i) slove(i);// 枚举root
    cout <<ans*qim(n,mod-2)%mod;
    return 0;    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值