BZOJ4675 点对游戏

根据期望的线性性(orz xuruifan,我们一定要叫线性性,不能叫可加性),期望幸运点对数等于每个幸运点对被选中的概率之和

由于每个点都是等价的,所以每个点对也是等价的,所以我们只需要计算1个点对被选中的概率,乘以点对数再乘以2(因为是有序的)即可

一个点对被选中的概率等于他在每次操作下被选中的概率之和

假设这个点对是(x,y)

对于一个人,第i次操作一定会选择一个点,x被选中的概率是1/n

如果这次选择了x,在之前的i-1次操作中,一定不会选择x,所以每次会在n-1个点中随机选一个没被选中的,所以y被选中的概率是i-1/n-1

所以一个人的第i次操作对答案的贡献是(1/n)*(i-1/n-1)*幸运点对数×2

至于如何求幸运点对数,这是点分治的经典问题,可以在O(mn log n)时间内求出

其实我们也可以用类似BZOJ4543 POI2014 HOTEL加强版的方法来优化n^2的DP,在O(nm)的时间,O(n)的空间内求出

我写的是优化DP,因为我做这道题的时候脑抽,忘了这是点分治经典问题了-_-

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
using namespace std;
#define MAXN 50010
#define MAXM 1010
#define ll long long
#define INF 1000000000
#define MOD 1000000007
#define eps 1e-8
struct vec{
    int to;
    int fro;
};
vec mp[MAXN*2];
int tai[MAXN],cnt;
int g[MAXN];
int F[MAXN*8];
int *f[MAXN];
int *hat=F+2;
int d[MAXN];
int n,m;
int l[MAXN];
int son[MAXN];
double cs[5],ans[5];
int lw[MAXN];
int dep[MAXN];
inline void be(int x,int y){
    mp[++cnt].to=y;
    mp[cnt].fro=tai[x];
    tai[x]=cnt;
}
inline void bde(int x,int y){
    be(x,y);
    be(y,x);
}
void pre(int x,int F){
    int i,y;
    lw[x]=x;
    dep[x]=dep[F]+1;
    for(i=tai[x];i;i=mp[i].fro){
        y=mp[i].to;
        if(y==F){
            continue ;
        }
        pre(y,x);
        if(d[y]+1>d[x]){
            son[x]=y;
            lw[x]=lw[y];
            d[x]=d[y]+1;
        }
    }
    for(i=tai[x];i;i=mp[i].fro){
        y=mp[i].to;
        if(y!=son[x]&&y!=F){
            hat+=dep[lw[y]]-dep[y]+2;
            f[lw[y]]=hat;
            hat+=5;
        }
    }
    if(x==1){
        hat+=dep[lw[x]]-dep[x]+2;
        f[lw[x]]=hat;
    }
}
void dfs(int x,int F){
    int i,j,k,y;
    for(i=tai[x];i;i=mp[i].fro){
        y=mp[i].to;
        if(y==F){
            continue ;
        }
        dfs(y,x);
        if(y==son[x]){
            f[x]=f[y]-1;
            g[x]+=g[y];
            for(j=1;j<=m;j++){
                if(l[j]-1<=d[y]){
                    g[x]+=f[y][l[j]-1];
                }
            }
        }
    }
    f[x][0]=1;
    for(i=tai[x];i;i=mp[i].fro){
        y=mp[i].to;
        if(y==F||y==son[x]){
            continue ;
        }
        g[x]+=g[y];
        for(j=0;j<=d[y];j++){
            for(k=1;k<=m;k++){
                if(j<l[k]&&l[k]-j-1<=d[x]){
                    g[x]+=f[y][j]*f[x][l[k]-j-1];
                }
            }
        }
        for(j=0;j<=d[y];j++){
            f[x][j+1]+=f[y][j];
        }
    }
}
int main(){
    int i,x,y;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d",&l[i]);
    }
    for(i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        bde(x,y);
    }
    pre(1,0);
    dfs(1,0);
    for(i=1;i<=n;i++){
        ans[(i-1)%3+1]+=1.0/n*cs[(i-1)%3+1]*g[1]*2;
        cs[(i-1)%3+1]+=1.0/(n-1);
    }
    for(i=1;i<=3;i++){
        printf("%.2lf\n",ans[i]);
    }
    return 0;
}
  
/*
6 2
1 3
1 2
2 3
1 4
1 5
2 6
  
*/


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值