bzoj 1977 洛谷P4180 严格次小生成树

Description:

给定一张N个节点M条边的无向图,求该图的严格次小生成树。设最小生成树边权之和为sum,那么严格次小生成树就是边权之和大于sum的最小的一个

Input:

第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

Output:

包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

思路:先求出原图的最小生成树,然后继续从小到大枚举边(x,y),对于x,y用倍增求LCA的同时用一个ST表维护两点间的最大值,取最小的差值即可

书上好像用的是倍增LCA的同时用两个dp维护最大值和次大值,表示并看不怎么懂……

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
#define ll long long

int head[N], now;
struct edges{
    int u, to, next, w;
}edge[N<<1];
void add(int u, int v, int w){ edge[++now] = {u, v, head[u], w}; head[u] = now;}

struct input{
    int u, v, w;
}E[N];
int n, m, father[N], fa[N][25];
ll mx[N][25], d[N], tot;
bool vis[N];

int get(int x){
    if(x != father[x]) return father[x] = get(father[x]);
    return x;
}
bool cmp(input x, input y){ return x.w < y.w; }
void kruskal(){
    sort(E + 1, E + m + 1, cmp);
    for(int i = 1; i <= n; i++) father[i] = i;
    for(int i = 1; i <= m; i++){
        int x = get(E[i].u), y = get(E[i].v);
        if(x != y) father[y] = x, tot += E[i].w, add(E[i].u, E[i].v, E[i].w), add(E[i].v, E[i].u, E[i].w), vis[i] = 1;
    }
}
void dfs(int x,int pre, int deep){
    fa[x][0] = pre; d[x] = deep;
    for(int i = head[x]; i; i = edge[i].next){
        int v = edge[i].to;
        if(v == pre) continue;
        mx[v][0] = edge[i].w;
        dfs(v, x, deep + 1);
    }
}
void work(){
    for(int j = 1; j <= 20; j++)
      for(int i = 1; i <= n; i++){
        fa[i][j] = fa[fa[i][j - 1]][j - 1];
        mx[i][j] = max(mx[i][j - 1], mx[fa[i][j - 1]][j - 1]);
      }

}
ll getmax(int x,int y, int z){
    if(y == -1) return 0;
    if(mx[x][y] == z) return max(getmax(x, y - 1, z), getmax(fa[x][y - 1], y - 1, z));
    return mx[x][y];
}
ll query(int x, int y, int z){
    ll maxn = 0;
    if(d[x] < d[y]) swap(x, y);
    if(d[x] ^ d[y])
      for(int i = 20; i >= 0; i--)
        if(d[fa[x][i]] >= d[y]) maxn = max(maxn, getmax(x, i, z)), x = fa[x][i];
    if(x == y) return z - maxn;
    for(int i = 20; i >= 0; i--){
        if(fa[x][i] != fa[y][i]){
            maxn = max(maxn, max(getmax(x, i, z), getmax(y, i, z)));
            x = fa[x][i], y = fa[y][i];
        }
    }
    return z - max(maxn, max(getmax(x, 0, z), getmax(y, 0, z)));
}
int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++)
      scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
    kruskal();
    dfs(1, 1, 1);
    work();
    ll ans = 1e18;
    for(int i = 1; i <= m; i++)
      if(!vis[i]){
        ll tmp = query(E[i].u, E[i].v, E[i].w);
        if(tmp) ans = min(ans, tot + tmp);
      }
    printf("%lld\n", ans);
    return 0;
} 

原文地址:https://www.cnblogs.com/Rorshach/p/8684832.html

时间: 2024-10-10 01:04:13

bzoj 1977 洛谷P4180 严格次小生成树的相关文章

洛谷.4180.[模板]次小生成树Tree(Kruskal LCA 倍增)

构建完MST后,枚举非树边(u,v,w),在树上u->v的路径中找一条权值最大的边(权为maxn),替换掉它这样在 w=maxn 时显然不能满足严格次小.但是这个w可以替换掉树上严格小于maxn的次大边用倍增维护MST上路径的最大值.次大值,每条非树边的查询复杂度就为O(logn) ps:1.倍增更新次大值时,未必是从最大值转移,要先赋值较大的次大值,再与较小的那个最大值比较.2.maxn!=w时,是可以从maxn更新的(不能更新就是上面情况啊)倍增处理部分我还是在dfs里写吧 md改了一晚上

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

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

洛谷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)

洛谷 P4180 【模板】严格次小生成树[BJWC2010]【次小生成树】

严格次小生成树模板 算法流程: 先用克鲁斯卡尔求最小生成树,然后给这个最小生成树树剖一下,维护边权转点权,维护最大值和严格次大值. 然后枚举没有被选入最小生成树的边,在最小生成树上查一下这条边的两端点的路径上的最长边,如果最长边等于枚举到的边的边权,那么选次长边(没有次长边的话直接跳过),然后在最小生成树的权值上减去路径上最/次长边,加上当前枚举的边的边权 因为如果加入枚举的边的,那么就形成了一个环,需要断开一条边 注意一开始单点次小值赋为0 #include<iostream> #inclu

BZOJ 4385 洛谷3594 POI2015 WIL-Wilcze do?y

[题解] 手残写错调了好久QAQ...... 洛谷的数据似乎比较水.. n个正整数!!这很重要 这道题是个类似two pointer的思想,外加一个单调队列维护当前区间内长度为d的子序列中元素之和的最大值. 枚举右端点,如果左端点到右端点的元素和减去区间内长为d的子序列中元素和的最大值,大于给定的P,那么就把左端点向右挪. #include<cstdio> #include<algorithm> #define N 2000010 #define rg register #defi

P4180 严格次小生成树[BJWC2010]

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

P4180 严格次小生成树[BJWC2010] Kruskal,倍增

题目链接\(Click\) \(Here\). 题意就是要求一个图的严格次小生成树.以前被题面吓到了没敢做,写了一下发现并不难. 既然要考虑次小我们就先考虑最小.可以感性理解到一定有一种次小生成树,可以由最小生成树删一条边再加一条边得到.我们枚举加上去的这一条边,加上去以后原\(mst\)会成为一个基环树,想让它次小就在这个环里找一条最长的边(不包含新加进去的)删掉就好.放在树上来讲,就是找到\(u\)到\(v\)路径上的最大值.这样我们就有了非严格次小生成树. 严格要怎么处理?我们需要排除新加

BZOJ 1827 洛谷 2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gather

[题解] 很容易想到暴力做法,枚举每个点,然后对于每个点O(N)遍历整棵树计算答案.这样整个效率是O(N^2)的,显然不行. 我们考虑如果已知当前某个点的答案,如何快速计算它的儿子的答案. 显然选择它的儿子作为集合点,它的儿子的子树内的奶牛可以少走当前点到儿子节点的距离dis,不在它儿子的子树内的奶牛要多走dis. 那么我们维护每个节点的子树内的奶牛总数(即点权和),就可以快速进行计算了.效率O(N). 1 #include<cstdio> 2 #include<algorithm>

洛谷 P2709 BZOJ 3781 小B的询问

题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 输入输出格式 输入格式: 第一行,三个整数N.M.K. 第二行,N个整数,表示小B的序列. 接下来的M行,每行两个整数L.R. 输出格式: M行,每行一个整数,其中第i行的整数表示第i个询问的答案. 输入输出样例 输入样例#1: 6 4 3 1 3 2 1 1 3