BZOJ1977 [BeiJing2010组队]次小生成树 Tree

恩,归类上来讲的话。。。是一道非常好的noip题。。。只不过嘛、、、(此处省略100字)

然后将如何做:

首先Kruskal求出最小生成树。

我们其实可以发现严格的次小生成树不可能在MST上改两条边 <=> 只能改一条边。

那么如何改呢?

每次在MST中加入一条非树边,即不在MST的边,那么会形成一个环,只要找到换上的严格小于当前边权的最大值,删之,就形成了次小生成树的候选。

由Kruskal的算法保证加入的边权一定是环上最大的,因此我们要记录树链上的最大值和次大值(因为是严格小于)

而记录的方法就是倍增。。。noip难度。。。T T

对每条非树边都做一次即可。

复杂度大概是O(m * logm + n * logn + (m - n) * logn)

  1 /**************************************************************
  2     Problem: 1977
  3     User: rausen
  4     Language: C++
  5     Result: Accepted
  6     Time:1740 ms
  7     Memory:32628 kb
  8 ****************************************************************/
  9
 10 #include <cstdio>
 11 #include <algorithm>
 12
 13 using namespace std;
 14 typedef long long ll;
 15 const int N = 100001;
 16 const int M = 300001;
 17 struct data{
 18     int x, y, v;
 19     bool selected;
 20 }a[M];
 21 struct edge{
 22     int next, to ,v;
 23 }e[N * 2];
 24 struct tree_node{
 25     int dep, fa[17], d1[17], d2[17];
 26 }tr[N];
 27 inline bool operator < (const data a, const data b){
 28     return a.v < b.v;
 29 }
 30
 31 int n, m, cnt, tot, del = 1e9;
 32 int first[N], fa[N];
 33 ll ans;
 34
 35 inline int read(){
 36     int x = 0, sgn = 1;
 37     char ch = getchar();
 38     while (ch < ‘0‘ || ch > ‘9‘){
 39         if (ch == ‘-‘) sgn = -1;
 40         ch = getchar();
 41     }
 42     while (ch >= ‘0‘ && ch <= ‘9‘){
 43         x = x * 10 + ch - ‘0‘;
 44         ch = getchar();
 45     }
 46     return sgn * x;
 47 }
 48
 49 inline void add_edge(int x, int y, int z){
 50     e[++tot].next = first[x], first[x] = tot;
 51     e[tot].to = y, e[tot].v = z;
 52 }
 53
 54 void add_Edges(int X, int Y, int Z){
 55     add_edge(X, Y, Z);
 56     add_edge(Y, X, Z);
 57 }
 58
 59 int find_fa(int x){
 60     return x == fa[x] ? x : fa[x] = find_fa(fa[x]);
 61 }
 62
 63 void dfs(int p){
 64     int i, x, y, FA;
 65     for (i = 1; i <= 16; ++i){
 66         if (tr[p].dep < (1 << i)) break;
 67         FA = tr[p].fa[i - 1];
 68         tr[p].fa[i] = tr[FA].fa[i - 1];
 69         tr[p].d1[i] = max(tr[p].d1[i - 1], tr[FA].d1[i - 1]);
 70         if (tr[p].d1[i - 1] == tr[FA].d1[i - 1])
 71             tr[p].d2[i] = max(tr[p].d2[i - 1], tr[FA].d2[i - 1]);
 72         else {
 73             tr[p].d2[i] = min(tr[p].d1[i - 1], tr[FA].d1[i - 1]);
 74             tr[p].d2[i] = max(tr[p].d2[i - 1], tr[p].d2[i]);
 75             tr[p].d2[i] = max(tr[p].d2[i], tr[FA].d2[i - 1]);
 76         }
 77     }
 78     for (x = first[p]; x; x = e[x].next)
 79         if ((y = e[x].to) != tr[p].fa[0]){
 80             tr[y].fa[0] = p, tr[y].d1[0] = e[x].v, tr[y].dep = tr[p].dep + 1;
 81             dfs(y);
 82         }
 83 }
 84
 85 inline int lca(int x, int y){
 86     if (tr[x].dep < tr[y].dep) swap(x, y);
 87     int tmp = tr[x].dep - tr[y].dep, i;
 88     for (i = 0; i <= 16; ++i)
 89         if ((1 << i) & tmp) x = tr[x].fa[i];
 90     for (i = 16; i >= 0; --i)
 91         if (tr[x].fa[i] != tr[y].fa[i])
 92             x = tr[x].fa[i], y = tr[y].fa[i];
 93     return x == y ? x : tr[x].fa[0];
 94 }
 95
 96 void calc(int x, int f, int v){
 97     int mx1 = 0, mx2 = 0, tmp = tr[x].dep - tr[f].dep, i;
 98     for (i = 0; i <= 16; ++i)
 99         if ((1 << i) & tmp){
100             if (tr[x].d1[i] > mx1)
101                 mx2 = mx1, mx1 = tr[x].d1[i];
102             mx2 = max(mx2, tr[x].d2[i]);
103             x = tr[x].fa[i];
104         }
105     del = min(del, mx1 == v ? v - mx2 : v - mx1);
106 }
107
108 void work(int t, int v){
109     int x = a[t].x, y = a[t].y, f = lca(x, y);
110     calc(x, f, v);
111     calc(y, f, v);
112 }
113
114 int main(){
115     n = read(), m = read();
116     int i, f1, f2, TOT = 0;
117     for (i = 1; i <= m; ++i)
118         a[i].x = read(), a[i].y = read(), a[i].v = read();
119     for (i = 1; i <= n; ++i)
120         fa[i] = i;
121     sort(a + 1, a + m + 1);
122     for (i = 1; i <= m; ++i)
123         if ((f1 = find_fa(a[i].x)) != (f2 = find_fa(a[i].y))){
124             fa[f1] = f2;
125             ans += a[i].v;
126             a[i].selected = 1;
127             add_Edges(a[i].x, a[i].y, a[i].v);
128             ++TOT;
129             if (TOT == n - 1) break;
130         }
131     dfs(1);
132     for (i = 1; i <= m; ++i)
133         if (!a[i].selected)
134             work(i, a[i].v);
135     printf("%lld\n", ans + del);
136     return 0;
137 }

时间: 2024-12-26 17:07:46

BZOJ1977 [BeiJing2010组队]次小生成树 Tree的相关文章

bzoj1977 [BeiJing2010组队]次小生成树 Tree——严格次小生成树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1977 因为严格,所以要记录到 LCA 的一个次小值: 很快写好,然后改掉一堆错误后终于过了样例!然而交上去1WA: 又改了半天,还是WA,于是放弃,抄题解好久... 然而就在我调了一个小时终于锁定错误就在那个子函数里的时候才突然看到了自己的明显惊天大错误是怎么回事??!!!稍微改了一下下就完美AC... 不过还有点收获,把求各种层次的 f 放在 dfs 函数里会比单独拿出来再求一遍快 10

【BZOJ 1977】 [BeiJing2010组队]次小生成树 Tree

1977: [BeiJing2010组队]次小生成树 Tree Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 2313  Solved: 544 [Submit][Status][Discuss] Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格

1977: [BeiJing2010组队]次小生成树 Tree

题解:和cf的一道题比较类似 首先跑一个MST 对于这个树做树链剖分 枚举不在这个树上的边找严格小于这条边的最大边权值 然后求ans #include <bits/stdc++.h> #define ll long long const int MAXN=1e5+10; const int maxn=3e5+10; const int inf=1e9+20; using namespace std; ll read(){ ll x=0,f=1;char ch=getchar(); while(

洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

洛谷题目传送门 %%%天平巨佬和山楠巨佬%%% 他们的题解 思路分析 具体思路都在两位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 首先kruskal把最小生成树弄出来,因为要求次小生成树.至于为什么次小一定只在最小的基础上改变了一条边,我也不会证......打表找规律大法好 剩下的可以有一堆数据结构来维护最大值和次大值(原理两位巨佬都讲清楚了,这里只分析一下算法的优劣) 倍增+LCA 山楠巨佬的做法,我也写了这一种.复杂度\(O(MlogM(kruscal)+MlogN(

[BeiJing2010组队]次小生成树 Tree

Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题. Input 第一行包含两个整数N 和M,表示无向图的

BZOJ 1977 [BeiJing2010组队]次小生成树 Tree

严格次小生成树.一开始没有特批一圈都相等的情况,一直WA,十分难受. 先生成最小生成树,枚举每条非树边,连上它构成一个环,拆掉环上树边中最大的一条(若和该边相等则次大的一条)换上这条. 用倍增维护一条链上的最大边和次大边,倍增跑lca同时找出环上最大边和次大边,看能否更新答案. #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath

[Beijing2010组队]次小生成树Tree

小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) \sum_{e \in E_M}value(e)<\sum_{e \in E_S}value(e)∑e∈EM??value(e)<∑e∈ES??value(e) 这下小

洛谷P4180 [Beijing2010组队]次小生成树Tree

题目描述 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) \sum_{e \in E_M}value(e)<\sum_{e \in E_S}value(e)∑e∈EM??value(e)<∑e∈ES??value(e)

严格次小生成树(Bzoj1977:[Beijing2010组队]次小生成树)

非严格次小生成树 很简单,先做最小生成树 然后枚举没加入的边加入,替换掉这个环内最大的边 最后取\(min\) 严格次小生成树 还是一样的 可以考虑维护一个严格次大值 最大值和枚举的边相同就替换次大值的边 否则替换最大值的边 最后取\(min\) 裸题 Luogu 随你用各种姿势\(AC\) \(LCT\)常数大,但是好写,开\(O2\)可以过 # include <bits/stdc++.h> # define RG register # define IL inline # define