2021牛客寒假第六场】H-动态最小生成树 线段树优化Kruskal

2021牛客寒假第六场】H-动态最小生成树 线段树优化Kruskal


传送门: https://ac.nowcoder.com/acm/contest/9986/H
这一题大部分人都是暴力Kruskal冲过去的,额,应该是全部,但这显然不是最优解。

题意

题 意 就 像 题 目 一 样 , 动 态 修 改 和 查 找 最 小 生 成 树 。 题意就像题目一样,动态修改和查找最小生成树。
两 个 操 作 : 两个操作:

  • 修 改 第 x 条 边 的 连 接 点 和 边 权 修改第x条边的连接点和边权 x
  • 查 询 [ l , r ] 所 在 边 组 成 的 最 小 生 成 树 大 小 , 有 则 输 出 , 没 有 输 出 i m p o s s i b l e 查询[l,r]所在边组成的最小生成树大小,有则输出,没有输出impossible [l,r]impossible

思路

看 到 最 小 生 成 树 , 就 知 道 可 以 用 K r u s k a l 求 解 。 看到最小生成树,就知道可以用Kruskal求解。 Kruskal
修 改 还 是 修 改 , 不 过 查 询 的 时 候 就 要 用 一 次 K r u s k a l , 遇 到 及 其 恶 心 的 数 据 铁 T 。 修改还是修改,不过查询的时候就要用一次Kruskal,遇到及其恶心的数据铁T。 KruskalT
因 为 K r u s k a l 的 复 杂 度 是 和 边 数 m 有 关 的 。 因为Kruskal的复杂度是和边数m有关的。 Kruskalm

看 到 n 只 有 200 , m 有 30000 , 所 以 能 不 能 把 复 杂 度 往 n 上 靠 ? 答 案 是 可 以 的 。 看到n只有200,m有30000,所以能不能把复杂度往n上靠?答案是可以的。 n200m30000n

区 间 问 题 ? 线 段 树 ! 区间问题?\red{线段树!} 线!

线 段 树 维 护 什 么 呢 ? 针 对 每 一 个 节 点 , 我 们 维 护 一 个 数 组 , 这 个 数 组 里 保 存 的 是 生 成 树 的 边 的 集 合 。 线段树维护什么呢?针对每一个节点,我们维护一个数组,这个数组里保存的是生成树的边的集合。 线

所 以 我 们 建 树 是 b u i l d ( 1 , 1 , m ) , 根 据 边 建 树 , 而 不 是 点 。 所以我们建树是build(1,1,m),根据边建树,而不是点。 build(1,1,m)

每 次 更 新 的 时 候 都 要 重 新 组 织 最 小 生 成 树 并 保 存 , 就 是 代 码 中 的 p u s h _ u p 。 每次更新的时候都要重新组织最小生成树并保存,就是代码中的push\_up。 push_up

Code

#include "bits/stdc++.h"

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
typedef pair<ll, ll> pll;

#define endl "\n"
#define eb emplace_back
#define mem(a, b) memset(a , b , sizeof(a))

const ll INF = 0x3f3f3f3f;
const ll mod = 998244353;
// const ll mod = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
const double R = 0.57721566490153286060651209;

const int N = 1e5 + 10;

int n, m, q;

struct Edge {
    int u, v, w;
}e[N];

#define lc t[u].l
#define rc t[u].r

struct Tree {
    int l, r;
    int a[505];
}t[N];
int tot = 1;
int f[N];

int find(int x) {
    return f[x] == x ? x : f[x] = find(f[x]);
}

void push_up(int s, int u, int v) {
    int x = 1, y = 1, z = 1;
    for(int i = 1;i <= n; i++) f[i] = i, t[s].a[i] = 0;
    while((t[u].a[x] || t[v].a[y]) && z < n) {
        if(e[t[u].a[x]].w < e[t[v].a[y]].w) {
            int U = find(e[t[u].a[x]].u);
            int V = find(e[t[u].a[x]].v);
            if(U != V) {
                f[U] = V;
                t[s].a[z++] = t[u].a[x];
            }
            x++;
        }
        else {
            int U = find(e[t[v].a[y]].u);
            int V = find(e[t[v].a[y]].v);
            if(U != V) {
                f[U] = V;
                t[s].a[z++] = t[v].a[y];
            }
            y++;
        }
    }
}

void build(int u, int l, int r) {
    if(l == r) {
        t[u].a[1] = l;
        return ;
    }
    int mid = (l + r) / 2;
    t[u].l = ++tot; t[u].r = ++tot;
    build(lc, l, mid);
    build(rc, mid + 1, r);
    push_up(u, lc, rc);
}

void modify(int u, int l, int r, int p) {
    if(l == r) return ;
    int mid = (l + r) / 2;
    if(p <= mid) modify(lc, l, mid, p);
    else modify(rc, mid + 1, r, p);
    push_up(u, lc, rc);
}

void query(int u, int l, int r, int ql, int qr) {
    if(ql <= l && r <= qr) {
        push_up(tot + 2, tot + 1, u);
        memcpy(t[tot + 1].a, t[tot + 2].a, 4 * (n + 1));
        return ;
    }
    int mid = (l + r) / 2;
    if(ql <= mid) query(lc, l, mid, ql, qr);
    if(qr > mid)  query(rc, mid + 1, r, ql, qr);
}

void solve() {
    scanf("%d%d%d",&n,&m,&q);
    e[0].w = INF;
    for(int i = 1;i <= m; i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    build(1, 1, m);
    while(q--) {
        int opt; scanf("%d",&opt);
        if(opt == 1) {
            int x, y, z, w; scanf("%d%d%d%d",&x,&y,&z,&w);
            e[x].u = y; e[x].v = z; e[x].w = w;
            modify(1, 1, m, x);
        }
        else {
            int l, r; scanf("%d%d",&l,&r);
            if(r - l + 1 < n - 1) {
                printf("Impossible\n");
                continue;
            }
            for(int i = 1;i <= n; i++) {
                t[tot + 2].a[i] = t[tot + 1].a[i] = 0;
            }
            query(1, 1, m, l, r);
            if(!t[tot + 1].a[n - 1]) {
                printf("Impossible\n");
            }
            else {
                int ans = 0;
                for(int i = 1;i < n; i++) ans += e[t[tot + 1].a[i]].w;
                printf("%d\n",ans);
            }
        }
    }
}

signed main() {
    ios_base::sync_with_stdio(false);
    // cin.tie(nullptr);
    // cout.tie(nullptr);
#ifdef FZT_ACM_LOCAL
    freopen("../in.txt", "r", stdin);
    freopen("../out.txt", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值