hdu4126 Genghis Khan the Conqueror Prim + 树形dp

好题,学到了很多新姿势。

题意:在一棵mst上,修改一些边的值(此边有可能不在MST上),Q次操作(每次只是在原图上修改),求修改后的MST总和.

题解:首先Prim 求出MST    (n^2)

对于每次修改,即相当于把MST上一条边截掉,原来的MST变成了两棵树,可以证明修改后的新MST一定包含这两棵树,也就是说只需要找到连接两棵树的最短边即可。

好了,关键问题就是怎样找连接两棵树上的最短边。可以用(n^2)的dp进行预处理,然后每次o(1)查询即可。

我们用dp[i][j] 表示 树A(包含i的树) 到 树B(包含j的树)的最短边长。类似floyd的更新处理。具体看代码。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
const int N = 3000 + 10;
const int inf = 1e8;
vector<int> ed[N];
vector<int> son[N];
typedef long long ll;
ll map[N][N],sum;
int fa[N],dis[N],pre[N],n,m,u,v,w,q;
int dp[N][N];
void prim()
{
    sum = 0;
    for(int i=1;i<=n;i++) ed[i].clear();
    int now=1,min_p,min_dis;
    for(int i=1;i<=n;i++) dis[i] = inf;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(now != j &&dis[j] > 0 && map[now][j] < dis[j]){
                dis[j] = map[now][j];
                pre[j] = now;
            }
        }
        min_p = -1; min_dis = inf;
        dis[now] = -1;
        for(int j=1;j<=n;j++) if(dis[j] > 0 && dis[j] < min_dis){
            min_p = j;
            min_dis = dis[j];
        }
        if(min_p==-1) break;
        ed[min_p].push_back(pre[min_p]);
        ed[pre[min_p]].push_back(min_p);
        now = min_p;
        sum += min_dis;
    }
}
void bbu(int x,int pa)
{
    for(int i=0;i<ed[x].size();i++) if(ed[x][i]!=pa){
        son[x].push_back(ed[x][i]);
        fa[ed[x][i]] = x;
        bbu(ed[x][i],x);
    }
}
void build()
{
    for(int i=1;i<=n;i++) fa[i] = i;
    for(int i=1;i<=n;i++) son[i].clear();
    bbu(1,0);
}
//注意下面的更新
ll dfs(int p,int u,int fa) //求p到 u及u的子树的最短边
{
    ll ans = inf;
    for(int i=0;i<ed[u].size();i++) if(ed[u][i]!=fa)
    {
        int v = ed[u][i];
        int tmp = dfs(p,v,u);
        ans = min(ans,(ll)tmp);
        dp[u][v] = dp[v][u] = min(dp[u][v],tmp);
    }
    if(p != fa)
        ans = min(ans,map[p][u]);
    return ans;
}
void init()
{
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dp[i][j]=inf;
    for(int i=1;i<=n;i++)
        dfs(i,i,-1);
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0 && m==0) break;
        double ans = 0.0;
        for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) map[i][j] = inf ;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&u,&v,&w);
            u++; v++;
            map[u][v] = map[v][u] = w;
        }
        for(int i=1;i<=n;i++) map[i][i] = 0;
        prim();
        build();
        init();
        scanf("%d",&q);
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            u++;v++;
            int fl = 1;
            if(fa[u]!=v && fa[v]!=u) fl=0;
            if(fl==0)
                ans += sum;
            else
                ans += sum - map[u][v] + min(w , dp[u][v]);
        }
        printf("%.4f\n",ans/q);
    }
    return 0;
}
时间: 2024-11-07 07:19:53

hdu4126 Genghis Khan the Conqueror Prim + 树形dp的相关文章

hdu-4126 Genghis Khan the Conqueror(最小生成树+树形dp)

题目链接: Genghis Khan the Conqueror Time Limit: 10000/5000 MS (Java/Others)   Memory Limit: 327680/327680 K (Java/Others) Problem Description Genghis Khan(成吉思汗)(1162-1227), also known by his birth name Temujin(铁木真) and temple name Taizu(元太祖), was the fo

HDU 4126 Genghis Khan the Conqueror MST+树形dp

题意: 给定n个点m条边的无向图. 下面m行给出边和边权 下面Q个询问. Q行每行给出一条边(一定是m条边中的一条) 表示修改边权. (数据保证修改后的边权比原先的边权大) 问:修改后的最小生成树的权值是多少. 每个询问互相独立(即每次询问都是对于原图修改) 保证没有重边. 求:所有修改后的最小生成树权值的平均值. 思路: 首先跑一个最小生成树. 求得这个MST的权值 int mst; 对于每个询问(u.v,dis); 若(u,v) 不是MST上的边,则此时的权值就是 mst 否则我们断开树边(

hdu 4126 Genghis Khan the Conqueror hdu 4756 Install Air Conditioning 最小生成树

这两题思路一样.先说下题意. 第一道就是一张图,q个操作,每次将一个边x,y增大到z,求出此时的最小生成树的值w,输出这q个w的平均值. 第二道是一张完全图,但是有一条未知边不能选,求最小生成树最大可能是多少. 对于第一道题,先求出最小生成树,对于每个操作x,y,z,假设x,y不是树边,那么w不变,如果是树边,那么假设这条边连接了u,v两个点集,那么只要添上一条两个点集间所有边的最小的那条即可.但是复杂度为n3,所以为了降低复杂度,要预处理出这条最小边,用dp[ i ][ j ]表示i,j两集合

HDU 4126 Genghis Khan the Conqueror (树形DP+MST)

题意:给一图,n个点,m条边,每条边有个花费,给出q条可疑的边,每条边有新的花费,每条可疑的边出现的概率相同,求不能经过原来可疑边 (可以经过可疑边新的花费构建的边),注意每次只出现一条可疑的边,n个点相互连通的最小花费的期望. 析:要想连通先让他们连通起来,先构造出一个MST,然后再暴力,如果这个边不在这里面,那么花费不变,如果在里面,那我们需要知道是用原来的边最少, 还是再找一条边使他们连通起来,这里就要先预处理了,dp[i]j[i] 表示 左边的那个一半 i 和 右边那一半 j 的最长距离

HDU 4126 POJ 4006 Genghis Khan the Conqueror

题意: n(3000)个点的图  q(10^4)次操作  每次操作从原图更改一条边的权值  问q次操作后最小生成树的平均值是多少 思路: 先求最小生成树  然后讨论  如果更改的不是树边  则最小生成树不变  如果是树边  就要选择原图中的非树边和更改后的这条边其中较小的一个形成新树 难做的只有"是树边"这种情况  我们考虑  原图中的非树边与原树一定可以形成一个环  那么我们可以这样理解  只要断掉的边是环内的树边  那么都可以用这条非树边补上形成新树  也就是说  这条非树边覆盖了

HDU4126Genghis Khan the Conqueror(最小生成树+并查集)

Genghis Khan the Conqueror Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 327680/327680 K (Java/Others) Total Submission(s): 1687    Accepted Submission(s): 501 Problem Description Genghis Khan(成吉思汗)(1162-1227), also known by his birth name

【树形DP】 HDU 4756 Install Air Conditioning

通道 题意:给n个点,现在要使这n个点连通,并且要求代价最小.现在有2个点之间不能直接连通(除了第一个点),求最小代价 思路:先求mst,然后枚举边,对于生成树上的边替换,用树形dp O(N^2)求出每条生成树边的最小替代边.然后替换后的最大值 代码: #include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <algorithm> using

Install Air Conditioning HDU - 4756(最小生成树+树形dp)

Install Air Conditioning HDU - 4756 题意是要让n-1间宿舍和发电站相连 也就是连通嘛 最小生成树板子一套 但是还有个限制条件 就是其中有两个宿舍是不能连着的 要求所有情况中最大的那个 这是稠密图 用kruskal的时间会大大增加 所以先跑一遍prim 跑完之后对最小生成树里面的边去搜索(树形dp)我觉得dp就是搜索(虽然我菜到切不了dp题.) so dfs的过程我也叫做树形dp咯 dp[i][j]表示i和j不相连后 这两个部分距离最小的边 代码如下 #incl

HDU-2196 Computer (树形DP)

最近在看树形DP,这题应该是树形DP的经典题了,写完以后还是有点感觉的.之后看了discuss可以用树分治来做,以后再试一试. 题目大意 找到带权树上离每个点的最远点.︿( ̄︶ ̄)︿ 题解: 对于每一个点的最远点,就是以这个点为根到所有叶子节点的最长距离.但是如果确定根的话,除了根节点外,只能找到每个节点(度数-1)个子树的最大值,剩下一个子树是该节点当前的父亲节点. 所以当前节点的最远点在当前节点子树的所有叶子节点以及父亲节点的最远点上(当父亲节点的最远点不在当前节点的子树上时), 如果父亲节