Edge of Taixuan

本文介绍了一种利用线段树维护区间最小值的方法解决特定图论问题——在给定图中寻找最小生成树。通过将区间按权值排序并覆盖的方式优化计算过程,确保了最终结果的正确性和最优性。

Edge of Taixuan

题意:

给一个n个点的图,和m个区间,每个区间[L, r]中的任意两点都有一条权值为w的边,求删除一些边以后所构成的最小生成树。

题解:

​ 当前[L, r]区间内的点u所连接的线的权值是w,假设多个区间都覆盖到了u这个点那么u所连的线的权值一定是其中wi最小的区间,假设不是这个权值为wi的线,因为当前这个线的作用是就是让u和别的联通,那么其他的一定比wi大,且作用相同,所以选择最小区间是最优的。

​ 所以用线段树来维护一个区间最小值,将m个区间按照权值从大到小排序,靠后的区间直接覆盖靠前的区间即可。最后如果存在一个点的mn为INF则证明没有任何一个区间覆盖住这个点,这个网络就不合法。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <unordered_map>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int n, m, k;
int cnt;
struct Seg {
    int l, r;
    int mn, lazy;
}tr[N << 2];
struct Node {
    int l, r, w;
}e[N];
bool cmp(Node a, Node b) {
    return a.w > b.w;
}
LL res, sum;
bool ok;
void down(int u) {
    if (tr[u].lazy) {
        tr[u << 1].mn = tr[u << 1].lazy = tr[u].lazy;
        tr[u << 1 | 1].mn = tr[u << 1 | 1].lazy = tr[u].lazy;
        tr[u].lazy = 0;
    }
}
void build(int u, int l, int r) {
    tr[u] = {l, r, INF, 0};
    if (l == r) return ;
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
void modify(int u, int l, int r, int v) {
    if (l <= tr[u].l && tr[u].r <= r) {
        tr[u].lazy = v;
        tr[u].mn = v;
        return ;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    down(u);
    if (l <= mid) modify(u << 1, l, r, v);
    if (r > mid)  modify(u << 1 | 1, l, r, v);
}
int query(int u, int pos) {
    if (tr[u].l == tr[u].r) return tr[u].mn;
    int mid = tr[u].l + tr[u].r >> 1;
    down(u);
    if (pos <= mid) return query(u << 1, pos);
    else            return query(u << 1 | 1, pos);
}
int main() {
  //  ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        res = sum = ok = false;
        scanf("%d%d", &n, &m);
        build(1, 1, n - 1);
        for (int i = 1; i <= m; i ++ ) {
            scanf("%d%d%d", &e[i].l, &e[i].r, &e[i].w);
            res += 1ll *  e[i].w * (e[i].r - e[i].l + 1) * (e[i].r - e[i].l) / 2;
        }
        sort(e + 1, e + m + 1, cmp); 
        for (int i = 1; i <= m; i ++ ) modify(1, e[i].l, e[i].r - 1, e[i].w);
        for (int i = 1; i < n; i ++ ) {
            int val = query(1, i);
            if (val == INF) ok = true;
            sum += val;
        }
        printf("Case #%d: ", ++ cnt);
        if (ok) printf("Gotta prepare a lesson");
        else    printf("%ld", res - sum);
        if (T) cout << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值