Codeforces Round #728 (Div. 2) D. Tree Array(概率期望+dp+LCA)
题意:
给出一个有 n n n个节点的树,一开始所有节点都未被标记。随后标记规则如下:
1. 1. 1. 一开始从 n n n个节点中等概率的选择一个节点标记。
2. 2. 2. 随后只能在有连向被标记的点的那些点中等概率的选择一个点然后标记。
每标记一个点,则将其加入到数组末尾,求最后数组的逆序对的期望。
题解:
因为随后选择的点必须要与一个已经被标记的点相连,所以可以将第一个选择的点当做根节点,然后考虑暴力根节点和两个点 ( u , v ) (u,v) (u,v) ( v > u ) (v>u) (v>u) ,求这两个点组成逆序对的概率。那么怎么求这个概率?
设 z z z表示 u u u和 v v v 的最近公共祖先,那么只有当 z z z被标记完之后,才能选择先标记 u u u还是 v v v ,其实就是两个分支,看哪个分支先标记完。设 x x x表示 u u u到 z z z的距离, y y y表示 v v v到 z z z的距离,那么要想构成逆序对,就得先标记 v v v,也就是先走完 y y y的距离。
设 f [ i ] [ j ] f[i][j] f[i][j] 表示两条链长分为别 i i i和 j j j ,且先走完 i i i的概率。那么状态转移就是:
f [ i ] [ j ] = f [ i − 1 ] [ j ] ⋅ 1 / 2 + f [ i ] [ j − 1 ] ⋅ 1 / 2 f[i][j]=f[i-1][j] \cdot 1/2+f[i][j-1] \cdot 1/2 f[i][j]=f[i−1][j]⋅1/2+f[i][j−1]⋅1/2 初始化 f [ 0 ] [ j ] = 1 f[0][j]=1 f[0][j]=1
最后分类讨论 z z z是否等于 u u u或 v v v 。若 z z z等于 v v v,那么一定先标记 v v v,那么概率 + = 1 +=1 +=1 ,若都不等,则 + = f [ y ] [ x ] +=f[y][x] +=f[y][x]。即可求出两个点之间构成逆序对的概率,然后求期望即可。
代码:
#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int MAXN=205;
const int inf=0x3f3f3f3f;
int head[MAXN];
int fa[MAXN][60],depth[MAXN];
int cnt=0,n;
int dis[MAXN];
ll f[MAXN][MAXN];
struct node
{
    int to;
    int next;
    int cost;
}edge[MAXN*2];
ll quick_pow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return ans;
}
ll inv(ll a,ll b)
{
    return (a*quick_pow(b,mod-2))%mod;
}
void add(int u,int v,int w)
{
    edge[cnt].to=v;
    edge[cnt].cost=w;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int u, int pre, int d){
    fa[u][0] = pre, depth[u] = d;
    for(int i = head[u]; i != -1; i = edge[i].next){
        int to = edge[i].to;
        if(to != pre) {dis[to]=dis[u]+edge[i].cost;dfs(to, u, d + 1);}
    }
}
void init(int root){       //初始化
    dfs(root, -1, 0);
    for(int j = 0; (1 << (j + 1)) < n; j++){
        for(int i = 1; i <= n; i++){
            if(fa[i][j] < 0) fa[i][j + 1] = -1;
            else fa[i][j + 1] = fa[fa[i][j]][j];
        }
    }
}
int LCA(int u, int v){
    if(depth[u] > depth[v]) swap(u, v);
    int temp = depth[v] - depth[u];
    for(int i = 0; (1 << i) <= temp; i++){
        if((1 << i) & temp) v = fa[v][i];
   }
    if(v == u) return u;
    for(int i = log2(n); i >= 0; i--){
        if(fa[u][i] != fa[v][i]){
            u = fa[u][i], v = fa[v][i];
        }
    }
    return fa[u][0];
}
ll solve()
{
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            int z=LCA(i,j);
            int x=dis[i]-dis[z];
            int y=dis[j]-dis[z];
            if(j==z){
                ans=(ans+1)%mod;
            }
            else if(z!=i){
                ans=(ans+f[y][x])%mod;
            }
            //cout<<i<<" "<<j<<" "<<ans<<endl;
        }
    }
    return ans;
}
int main()
{
    memset(head,-1,sizeof head);
    cin>>n;
    for(int i=1;i<=n-1;i++){
        int u,v;
        cin>>u>>v;
        add(u,v,1);
        add(v,u,1);        
    }
    for(int j=1;j<=n;j++){
        f[0][j]=1;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            f[i][j]=inv(f[i-1][j],2)+inv(f[i][j-1],2);
            //cout<<i<<" "<<j<<f[i][j]<<endl;
        }
    }
    ll ans=0;
    //cout<<"11"<<endl;
    for(int i=1;i<=n;i++){
        init(i);
        ll res=solve();
        //cout<<res<<endl;
        ans=(ans+inv(res,n))%mod;
    }
    cout<<ans<<endl;
}

 
                
             
         浙公网安备 33010602011771号
浙公网安备 33010602011771号