HDU 4126 Genghis Khan the Conqueror 类似于4756的树形DP

题意:有一个无向图,存在一个最小生成树,然后给q次询问,即把一条边的权值改成一个更大的值,问换一条边之后的最小生成树的权值,然后求每次询问之后最小生成树权值的综合然后除以q。


想法:就是先求出最小生成树,首先我们知道如果删掉一条最小生成树的边,那么此时,最小生成树就会变成两个连通分量,那么我们就用dp[a][b]表示删除边a,b,之后连通分量之间的最短距离。显然从一个点进行dfs,可以遍历到最小生成树的每一条边,那么如果枚举到的这个边,就为此时我们要删除的边,那么树就自动被分成了两个连通分量。但是需要控制一个变量,就是设连通分量A中到B中所有点,距离最短的点为root,进行控制变量。

主要dfs的过程:说一下最重要的地方,就是为什么当root=fa时候不可以更新最短的距离。当删除u和root之间的边的时候,对于最小生成树来说,最短的就是这条边,现在要找的是除此以外,root为A集合控制点,到B集合的最短距离,即次短,如果要是不加这个条件,那么A到B的最短距离永远是root到u的距离,显然违背要删除此边的用意。


#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 0x7fffffff
using namespace std;
const int nodes=5000+100;
const int edges=5100*5100*4;
int n,m;
struct node
{
    int v,next;
}e[edges];
int head[nodes],cnt;
double map[nodes][nodes],dis[nodes],mst_sum,dp[nodes][nodes];
int pre[nodes],vis[nodes];
int used[nodes][nodes];
void Init()
{
    memset(head,-1,sizeof(head));
    memset(used,0,sizeof(used));
    cnt=0;
}
void add(int a,int b)
{
    e[cnt].v=b;
    e[cnt].next=head[a];
    head[a]=cnt++;
}
double Min(double a,double b)
{
    if(a<b) return a;
    return b;
}
void prime()
{
    mst_sum=0;
    for(int i=0;i<n;i++)
    {
        dis[i]=map[0][i];
        pre[i]=0;
    }
    memset(vis,0,sizeof(vis));
    vis[0]=1;
    pre[0]=-1;
    for(int i=1;i<n;i++)
    {
        double minn=(double)inf;
        int pos;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&minn>dis[j])
            {
                minn=dis[j];
                pos=j;
            }
        }
        vis[pos]=1;
        used[pre[pos]][pos]=used[pos][pre[pos]]=1;
        add(pre[pos],pos);add(pos,pre[pos]);
        mst_sum+=minn;
        for(int j=0;j<n;j++)
        {
            if(!vis[j]&&dis[j]>map[pos][j])
            {
                dis[j]=map[pos][j];
                pre[j]=pos;
            }
        }
    }
}
double dfs(int root,int u,int fa)
{
    double res=(double)inf;
    for(int i=head[u];i+1;i=e[i].next)
    {
        int v=e[i].v;
        if(v==fa) continue;
        double tmp=dfs(root,v,u);
        dp[u][v]=dp[v][u]=Min(dp[u][v],tmp);
        res=Min(res,tmp);
    }
    if(fa!=root)
    {
        res=Min(res,map[root][u]);
    }
    return res;
}
int main()
{
    while(~scanf("%d%d",&n,&m),n+m)
    {
        Init();
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(i==j) map[i][j]=0;
                else map[i][j]=inf;
                dp[i][j]=inf;
            }
        }
        for(int i=1;i<=m;i++)
        {
            int a,b;
            double c;
            scanf("%d%d%lf",&a,&b,&c);
            map[a][b]=map[b][a]=c;
        }
        prime();
        for(int i=0;i<n;i++)
        {
            dfs(i,i,-1);
        }
        int q;
        double sum=0;
        scanf("%d",&q);
        for(int k=1;k<=q;k++)
        {
            int a,b;
            double c;
            scanf("%d%d%lf",&a,&b,&c);
            double res;
            if(used[a][b])
            {
                if(dp[a][b]<c)
                {
                    res=mst_sum-map[a][b]+dp[a][b];
                }
                else 
                {
                    res=mst_sum-map[a][b]+c;
                }
            }
            else res=mst_sum;
            sum+=res;
        }
        printf("%.4lf\n",sum/q);
    }
    return 0;
} 

你可能感兴趣的:(HDU 4126 Genghis Khan the Conqueror 类似于4756的树形DP)