zoukankan      html  css  js  c++  java
  • 【BZOJ2521】 [Shoi2010]最小生成树

    Description

    Secsa最近对最小生成树问题特别感兴趣。他已经知道如果要去求出一个n个点、m条边的无向图的最小生成树有一个Krustal算法和另一个Prim的算法。另外,他还知道,某一个图可能有多种不同的最小生成树。例如,下面图 3中所示的都是图 2中的无向图的最小生成树:

     

    当然啦,这些都不是今天需要你解决的问题。Secsa想知道对于某一条无向图中的边AB,至少需要多少代价可以保证AB边在这个无向图的最小生成树中。为了使得AB边一定在最小生成树中,你可以对这个无向图进行操作,一次单独的操作是指:先选择一条图中的边 P1P2,再把图中除了这条边以外的边,每一条的权值都减少1。如图 4所示就是一次这样的操作:

     

    Input

    输入文件的第一行有3个正整数n、m、Lab分别表示无向图中的点数、边数、必须要在最小生成树中出现的AB边的标号。
    接下来m行依次描述标号为1,2,3…m的无向边,每行描述一条边。每个描述包含3个整数x、y、d,表示这条边连接着标号为x、y的点,且这条边的权值为d。
    输入文件保证1<=x,y<=N,x不等于y,且输入数据保证这个无向图一定是一个连通图。

    Output

    输出文件只有一行,这行只有一个整数,即,使得标号为Lab边一定出现最小生成树中的最少操作次数。

    Sample Input

    4 6 1
    1 2 2
    1 3 2
    1 4 3
    2 3 2
    2 4 4
    3 4 5

    Sample Output

    1

    HINT

    第1个样例就是问题描述中的例子。


    1<=n<=500,1<=M<=800,1<=D<10^6

    Source

    day2

    Solution

    思路很神的一道题。

    首先,其他所有边权值-1可以看做这条边+1。如果选定的边本来就在最小生成树上就不用管它。

    如果不在MST上的话,就要考虑想办法让它在MST上。让它在MST上的条件是s,t两个联通块之间一定不存在权值比它更小的边。

    对于所有比选定的边边权小的边,让它一定不出现在MST上的代价就是让它的边权变为选定的边权+1。问题就变成了选定一些边使得s和t不连通,且边权总和最小。然后就变成了最小割模型,用网络流来解决。

    Code

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 
     5 #define maxn 510
     6 #define maxm 810
     7 #define R register
     8 #define dmin(_a, _b) ((_a) < (_b) ? (_a) : (_b))
     9 #define inf 0x7fffffff
    10 int n;
    11 struct edge {int a, b, w; } ee[maxm];
    12 struct Edge {
    13     Edge *next, *rev;
    14     int to, cap;
    15 } *cur[maxn], *last[maxn], e[maxm << 2], *ecnt = e;
    16 inline void link(R int a, R int b, R int w)
    17 {
    18     *++ecnt = (Edge) {last[a], ecnt + 1, b, w}; last[a] = ecnt;
    19     *++ecnt = (Edge) {last[b], ecnt - 1, a, 0}; last[b] = ecnt;
    20 }
    21 int s, t, dep[maxn], q[maxn], ans;
    22 inline bool bfs()
    23 {
    24     R int head = 0, tail = 1;
    25     memset(dep, -1, (n + 1) << 2);
    26     dep[q[1] = t] = 0;
    27     while (head < tail)
    28     {
    29         R int now = q[++head];
    30         for (R Edge *iter = last[now]; iter; iter = iter -> next)
    31             if (iter -> rev -> cap && dep[iter -> to] == -1)
    32                 dep[q[++tail] = iter -> to] = dep[now] + 1;
    33     }
    34     return dep[s] != -1;
    35 }
    36 int dfs(R int x, R int f)
    37 {
    38     if (x == t) return f;
    39     R int used = 0;
    40     for (R Edge* &iter = cur[x]; iter; iter = iter -> next)
    41         if (iter -> cap && dep[iter -> to] + 1 == dep[x])
    42         {
    43             R int v = dfs(iter -> to, dmin(f - used, iter -> cap));
    44             iter -> cap -= v;
    45             iter -> rev -> cap += v;
    46             used += v;
    47             if (used == f) return f;
    48         }
    49     return used;
    50 }
    51 inline void dinic()
    52 {
    53     while (bfs())
    54     {
    55         memcpy(cur, last, sizeof cur);
    56         ans += dfs(s, inf);
    57     }
    58 }
    59 int main()
    60 {
    61     R int m, lab; scanf("%d%d%d", &n, &m, &lab);
    62     for (R int i = 1; i <= m; ++i) scanf("%d%d%d", &ee[i].a, &ee[i].b, &ee[i].w);
    63     for (R int i = 1; i <= m; ++i)
    64         if (ee[i].w <= ee[lab].w && i != lab)
    65         {
    66             link(ee[i].a, ee[i].b, ee[lab].w - ee[i].w + 1);
    67             link(ee[i].b, ee[i].a, ee[lab].w - ee[i].w + 1);
    68         }
    69     s = ee[lab].a; t = ee[lab].b;
    70     dinic();
    71     printf("%d
    ", ans);
    72     return 0;
    73 }
  • 相关阅读:
    判断字符串是否包含某一个值方法
    生成一个随机手机号
    python 常用数据类型及操作方法
    selenium 常用的定位方法
    mysql 多表查询
    转载--python全栈开发学习
    Jenkins执行jmeter自动化脚本方法
    阅读方法
    pytorch学习
    英文阅读材料记录
  • 原文地址:https://www.cnblogs.com/cocottt/p/6781823.html
Copyright © 2011-2022 走看看