BZOJ 2521: [Shoi2010]最小生成树

2521: [Shoi2010]最小生成树

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 445  Solved: 262
[Submit][Status][Discuss]

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

[Submit][Status][Discuss]

题目中的操作——将除这条边外所有其他边的权值全部+1——就是忽悠人的,等价于将这条边的权值+1。

利用Kruskal算法的思想,如果将所有边按照权值从小到大排序后,排在指定边之前(包括和指定边权值相同)的边能使得指定边的两点联通,则指定边一定不会被选中。将一条边从指定边之前移走的最小代价就是使得其变得严格大于指定边,插值是$Val_{id}-Val_{i}+1$。把代价作为容量,跑最小割即可。

  1 #include <cstdio>
  2 #include <cstring>
  3
  4 inline char nextChar(void)
  5 {
  6     static const int siz = 1 << 20;
  7
  8     static char buf[siz];
  9     static char *hd = buf + siz;
 10     static char *tl = buf + siz;
 11
 12     if (hd == tl)
 13         fread(hd = buf, 1, siz, stdin);
 14
 15     return *hd++;
 16 }
 17
 18 inline int nextInt(void)
 19 {
 20     register int ret = 0;
 21     register bool neg = false;
 22     register char bit = nextChar();
 23
 24     for (; bit < 48; bit = nextChar());
 25         if (bit == ‘-‘)neg ^= true;
 26
 27     for (; bit > 47; bit = nextChar())
 28         ret = ret * 10 + bit - ‘0‘;
 29
 30     return neg ? -ret : ret;
 31 }
 32
 33 const int siz = 2005;
 34 const int edg = 2e6 + 5;
 35 const int inf = 2e9 + 7;
 36
 37 int n, m, id, s, t;
 38
 39 struct edge
 40 {
 41     int x, y, w;
 42 }e[edg];
 43
 44 int hd[siz], to[edg], nt[edg], fl[edg], tot;
 45
 46 inline void add(int u, int v, int f)
 47 {
 48     nt[tot] = hd[u]; to[tot] = v; fl[tot] = f; hd[u] = tot++;
 49     nt[tot] = hd[v]; to[tot] = u; fl[tot] = 0; hd[v] = tot++;
 50 }
 51
 52 int dep[siz];
 53
 54 inline bool bfs(void)
 55 {
 56     static int que[siz];
 57     static int head, tail;
 58
 59     memset(dep, 0, sizeof(dep));
 60
 61     que[head = 0] = s, tail = dep[s] = 1;
 62
 63     while (head != tail)
 64     {
 65         int u = que[head++], v;
 66
 67         for (int i = hd[u]; ~i; i = nt[i])
 68             if (!dep[v = to[i]] && fl[i])
 69                 dep[que[tail++] = v] = dep[u] + 1;
 70     }
 71
 72     return dep[t];
 73 }
 74
 75 int cur[siz];
 76
 77 inline int min(int a, int b)
 78 {
 79     return a < b ? a : b;
 80 }
 81
 82 int dfs(int u, int f)
 83 {
 84     if (!f || u == t)
 85         return f;
 86
 87     int used = 0, flow, v;
 88
 89     for (int i = cur[u]; ~i; i = nt[i])
 90         if (dep[v = to[i]] == dep[u] + 1 && fl[i])
 91         {
 92             flow = dfs(v, min(fl[i], f - used));
 93
 94             used += flow;
 95             fl[i] -= flow;
 96             fl[i^1] += flow;
 97
 98             if (fl[i])
 99                 cur[u] = i;
100
101             if (used == f)
102                 return f;
103         }
104
105     if (!used)
106         dep[u] = 0;
107
108     return used;
109 }
110
111 inline int minCut(void)
112 {
113     int minCut = 0, newFlow;
114
115     while (bfs())
116     {
117         memcpy(cur, hd, sizeof(hd));
118
119         while (newFlow = dfs(s, inf))
120             minCut += newFlow;
121     }
122
123     return minCut;
124 }
125
126 signed main(void)
127 {
128     n = nextInt();
129     m = nextInt();
130
131     id = nextInt();
132
133     for (int i = 1; i <= m; ++i)
134     {
135         e[i].x = nextInt();
136         e[i].y = nextInt();
137         e[i].w = nextInt();
138     }
139
140     s = e[id].x;
141     t = e[id].y;
142
143     int lim = e[id].w;
144
145     memset(hd, -1, sizeof(hd));
146
147     for (int i = 1; i <= m; ++i)
148         if (e[i].w <= lim && i != id)
149             add(e[i].x, e[i].y, lim + 1 - e[i].w),
150             add(e[i].y, e[i].x, lim + 1 - e[i].w);
151
152     printf("%d\n", minCut());
153 }

@Author: YouSiki

时间: 2024-10-01 00:23:17

BZOJ 2521: [Shoi2010]最小生成树的相关文章

【BZOJ2521】[Shoi2010]最小生成树 最小割

[BZOJ2521][Shoi2010]最小生成树 Description Secsa最近对最小生成树问题特别感兴趣.他已经知道如果要去求出一个n个点.m条边的无向图的最小生成树有一个Krustal算法和另一个Prim的算法.另外,他还知道,某一个图可能有多种不同的最小生成树.例如,下面图 3中所示的都是图 2中的无向图的最小生成树: 当然啦,这些都不是今天需要你解决的问题.Secsa想知道对于某一条无向图中的边AB,至少需要多少代价可以保证AB边在这个无向图的最小生成树中.为了使得AB边一定在

bzoj2521 [Shoi2010]最小生成树

[Shoi2010]最小生成树 Time Limit: 10 Sec Memory Limit: 128 MB Description Secsa最近对最小生成树问题特别感兴趣.他已经知道如果要去求出一个n个点.m条边的无向图的最小生成树有一个Krustal算法和另一个Prim的算法.另外,他还知道,某一个图可能有多种不同的最小生成树.例如,下面图 3中所示的都是图 2中的无向图的最小生成树: 当然啦,这些都不是今天需要你解决的问题.Secsa想知道对于某一条无向图中的边AB,至少需要多少代价可

BZOJ 2521 最小生成树(最小割)

http://www.lydsy.com/JudgeOnline/problem.php?id=2521 题意:每次能增加一条边的权值1,求最小代价让一条边保证在最小生成树里 思路:如果两个点中有环,那么这条边必须不能是环的最大边,这样子把之前所有的边权值变成V+1-v[i],无向图网络流就可以了 1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5

[BZOJ 1016] [JSOI2008] 最小生成树计数 【DFS】

题目链接:BZOJ - 1016 题目分析 最小生成树的两个性质: 同一个图的最小生成树,满足: 1)同一种权值的边的个数相等 2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性相等 这样,先做一次Kruscal求出每种权值的边的条数,再按照权值从小到大,对每种边进行 DFS, 求出这种权值的边有几种选法. 最后根据乘法原理将各种边的选法数乘起来就可以了. 特别注意:在DFS中为了在向下DFS之后消除决策影响,恢复f[]数组之前的状态,在DFS中调用的Find()函数不能路

BZOJ 1016: [JSOI2008]最小生成树计数

http://www.lydsy.com/JudgeOnline/problem.php?id=1016 题意: 思路: 一个无向图所有的最小生成树中某种权值的边的数目均相同. 引用一篇大牛的证明: 我们证明以下定理:一个无向图所有的最小生成树中某种权值的边的数目均相同. 开始时,每个点单独构成一个集合. 首先只考虑权值最小的边,将它们全部添加进图中,并去掉环,由于是全部尝试添加,那么只要是用这种权值的边能够连通的点,最终就一定能在一个集合中. 那么不管添加的是哪些边,最终形成的集合数都是一定的

BZOJ 1016 JSOI2008 最小生成树计数 Kruskal

题目大意:给定一个无向图,求最小生成树的方案数 首先对于一个无向图的最小生成树,每种边权的边的数量是一定的 首先我们先跑一遍Kruskal,求出最小生成树上每种边权的出现次数 然后对于每种出现在最小生成树上的边权,我们从小到大处理 对于每种边权,我们枚举这种边权的边有多少种方案可以加进最小生成树上而不形成环 这个用状压处理 ans乘上这个值 然后把这种边权连接的所有联通块缩点 注意最小生成树不存在时输出0 #include<map> #include<cstdio> #includ

bzoj 1016: [JSOI2008]最小生成树计数【dfs+克鲁斯卡尔】

有一个性质就是组成最小生成树总边权值的若干边权总是相等的 这意味着按边权排序后在权值相同的一段区间内的边能被选入最小生成树的条数是固定的 所以先随便求一个最小生成树,把每段的入选边数记录下来 然后对于每一段dfs找合法方案即可,注意dfs中需要退回并查集,所以用不路径压缩的并查集 然后根据乘法定理,把每一段dfs后的结果乘起来即可. #include<iostream> #include<cstdio> #include<algorithm> using namespa

BZOJ 1016 [JSOI2008]最小生成树计数 ——Matrix-Tree定理

考虑从小往大加边,然后把所有联通块的生成树个数计算出来. 然后把他们缩成一个点,继续添加下一组. 最后乘法原理即可. 写起来很恶心 #include <queue> #include <cmath> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace s

【BZOJ-2521】最小生成树 最小割

2521: [Shoi2010]最小生成树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 415  Solved: 242[Submit][Status][Discuss] Description Secsa最近对最小生成树问题特别感兴趣.他已经知道如果要去求出一个n个点.m条边的无向图的最小生成树有一个Krustal算法和另一个Prim的算法.另外,他还知道,某一个图可能有多种不同的最小生成树.例如,下面图 3中所示的都是图 2中的无向图的最小