hdu4126Genghis Khan the Conqueror

来源:互联网 发布:九年级下册历史书淘宝 编辑:程序博客网 时间:2024/04/26 18:39

链接: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 0
原创粉丝点击