hdu4126Genghis Khan the Conqueror

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4126

题意:给定n个点的m条边,其中有q条边中的一条一定会变大(每一条变大的概率相同),求变大之后最小生成树边权和的期望。

分析:最小生成树+树形dp的好题。首先我们要确定最初的最小生成树是有哪些边组成的,然后对于每一条可能变大的边进行判断,这样变大的边就会被分为两类A:变大的边不是最小生成树中的边,这时候显然最小生成树的权值和是不会变的,因为这条变大的边还是不会被加入到最小生成树中。B:变大的边恰好是最小生成树中的一条,这时候我们就得删掉原来这条边的权值,这样的话树中重新变成了两颗子树,我们应该继续用这条变大了的边还是在之前就没有用到的边中选一条最优的呢?这就是我们要解决的问题了。我们设dp[i][j]表示i不在以j为根的子树中时i通过没有用过的边距离j子树中所有点中的最小值,那么有dp[i][j]=min(dis[i][j],dis[i][k]),k是j的儿子并且要求i到这些点直接的边在最小生成树中没用过,我们反过来就能求出DP[j]=min(dp[i][j])表示去掉j与j的父亲间的边时最小的备用边,处理出来DP数组我们就能O(1)解决每个询问啦。O(n^2)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<vector>
#include<string>
#include<stdio.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=3010;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const int INF=1000000010;
const ll MAX=1000000000;
const double pi=acos(-1.0);
typedef double db;
typedef unsigned long long ull;
ll sum,ans;
bool q[N][N],bo[N];
int n,m,tot,u[N],v[2*N],pre[2*N];
int in[N],out[N],fa[N],DI[N],dis[N][N],dp[N][N],DP[N];
void add(int a,int b) {
    v[tot]=b;pre[tot]=u[a];u[a]=tot++;
    v[tot]=a;pre[tot]=u[b];u[b]=tot++;
}
void dfs(int a,int b) {
    in[a]=++tot;fa[a]=b;
    for (int i=u[a];i!=-1;i=pre[i])
    if (v[i]!=b) dfs(v[i],a);
    out[a]=tot;
    for (int i=1;i<=n;i++)
    if (!(in[i]>=in[a]&&out[i]<=out[a])) {
        if (!q[i][a]) dp[i][a]=dis[i][a];
        for (int j=u[a];j!=-1;j=pre[j])
        if (!q[i][v[j]]&&v[j]!=b) dp[i][a]=min(dp[i][a],dp[i][v[j]]);
    }
}
void init() {
    int i,j,x,y,z;
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++) dis[i][j]=1e8;
    for (i=1;i<=n;i++) dis[i][i]=0;
    for (i=1;i<=m;i++) {
        scanf("%d%d%d", &x, &y, &z);
        x++;y++;dis[x][y]=dis[y][x]=z;
    }
}
void prim() {
    int i,j,w,mi;
    memset(q,0,sizeof(q));
    for (i=1;i<=n;i++) q[i][i]=1;
    tot=0;sum=0;
    memset(u,-1,sizeof(u));
    memset(bo,0,sizeof(bo));
    for (i=1;i<=n;i++) fa[i]=1,DI[i]=dis[1][i];
    for (i=1;i<=n;i++) {
        w=0;mi=1e8;
        for (j=1;j<=n;j++)
        if (!bo[j]&&DI[j]<mi) w=j,mi=DI[j];
        sum+=mi;bo[w]=1;add(w,fa[w]);
        q[w][fa[w]]=q[fa[w]][w]=1;
        for (j=1;j<=n;j++)
        if (dis[w][j]<DI[j]) DI[j]=dis[w][j],fa[j]=w;
    }
}
void deal() {
    int i,j;
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++) dp[i][j]=1e8;
    for (i=1;i<=n;i++) in[i]=out[i]=-1;
    tot=0;dfs(1,1);
    for (i=1;i<=n;i++) DP[i]=1e8;
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
        if (!(in[j]>=in[i]&&out[j]<=out[i])) DP[i]=min(DP[i],dp[j][i]);
}
void query() {
    int i,x,y,z;
    scanf("%d", &m);ans=0;
    for (i=1;i<=m;i++) {
        scanf("%d%d%d", &x, &y, &z);
        x++;y++;
        if (!q[x][y]) ans+=sum;
        else if (fa[x]==y) ans+=sum-dis[x][y]+min(DP[x],z);
            else ans+=sum-dis[x][y]+min(DP[y],z);
    }
    printf("%.4f\n", 1.0*ans/m);
}
int main()
{
    while (scanf("%d%d", &n, &m)&&(n||m)) {
        init();prim();
        deal();query();
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值