P4172 [WC2006]水管局长

[LCT]P4172水管局长

\(Solution\)

如果没有删除,那么就是维护一个最小生成树,然后倍增求两点之间的最大边权(货车运输).

因为有删边操作,想到LCT,但这是删除,最大值不满足减法,所以不好搞。

但注意到只有删除没有添加,所以我们可以倒过来处理,一条一条边link维护最小生成树以及两点间最大值,这样最大值就变成可加的了。

倒着处理,如果要加一条边u->v,那么先查询u->v的最大边权(假如这条边为x->y),如果要加的边权比这还大,那么就忽略(贪心),否则就删掉x->y,再link(u, v). (ps:因为要知道x->y对应的边是什么,所以可以用 map<pair<int, int> >)

一个小技巧:因为LCT维护的是点上的信息,所以我们边化点.如果要连xy,且这条边编号z,那么就执行link(x, z + n), link(z + n, y)

\(Source\)

#include <map>
#include <cmath>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <assert.h>
#include <algorithm>

using namespace std;

#define fir first
#define sec second
#define pb push_back
#define mp make_pair
#define LL long long
#define INF (0x3f3f3f3f)
#define mem(a, b) memset(a, b, sizeof (a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define Debug(x) cout << #x << " = " << x << endl
#define tralve(i, x) for (register int i = head[x]; i; i = nxt[i])
#define For(i, a, b) for (register int (i) = (a); (i) <= (b); ++ (i))
#define Forr(i, a, b) for (register int (i) = (a); (i) >= (b); -- (i))
#define file(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)

namespace io {
    static char buf[1<<21], *pos = buf, *end = buf;
    inline char getc()
    { return pos == end && (end = (pos = buf) + fread(buf, 1, 1<<21, stdin), pos == end) ? EOF : *pos ++; }
    inline int rint() {
        register int x = 0, f = 1;register char c;
        while (!isdigit(c = getc())) if (c == '-') f = -1;
        while (x = (x << 1) + (x << 3) + (c ^ 48), isdigit(c = getc()));
        return x * f;
    }
    inline LL rLL() {
        register LL x = 0, f = 1; register char c;
        while (!isdigit(c = getc())) if (c == '-') f = -1;
        while (x = (x << 1ll) + (x << 3ll) + (c ^ 48), isdigit(c = getc()));
        return x * f;
    }
    inline void rstr(char *str) {
        while (isspace(*str = getc()));
        while (!isspace(*++str = getc()))
            if (*str == EOF) break;
        *str = '\0';
    }
    template<typename T>
        inline bool chkmin(T &x, T y) { return x > y ? (x = y, 1) : 0; }
    template<typename T>
        inline bool chkmax(T &x, T y) { return x < y ? (x = y, 1) : 0; }
}
using namespace io;

const int N = 3e5 + 10;

int n, m;
struct Edge { int x, y, z; } edge[N];
bool cmp (Edge a, Edge b) { return a.z < b.z; }
struct Query { int op, x, y, id;} q[N];
map< pair<int, int>, int> Id;
bool del[N];

namespace LCT {
#define ls (ch[x][0])
#define rs (ch[x][1])
#define chk(x) (ch[fa[x]][1] == x)
    int val[N], mx[N], ch[N][2], fa[N], rev[N];
    bool isroot(int x) { return x != ch[fa[x]][0] && x != ch[fa[x]][1]; }
    void pushup(int x) {
        mx[x] = val[x];
        if (edge[mx[x]].z < edge[mx[ch[x][0]]].z) mx[x] = mx[ch[x][0]];
        if (edge[mx[x]].z < edge[mx[ch[x][1]]].z) mx[x] = mx[ch[x][1]];
    }
    void pushdown(int x) {
        if (rev[x]) {
            swap(ch[ls][0], ch[ls][1]); swap(ch[rs][0], ch[rs][1]);
            rev[ls] ^= 1, rev[rs] ^= 1;
            rev[x] = 0;
        }
    }
    void rotate(int x) {
        int y = fa[x], z = fa[y], k = chk(x), tmp = ch[x][k ^ 1];
        ch[y][k] = tmp, fa[tmp] = y;
        if (!isroot(y)) ch[z][chk(y)] = x; fa[x] = z;
        ch[x][k ^ 1] = y, fa[y] = x;
        pushup(y); pushup(x);
    }
    int stk[N], top;
    void splay(int x) {
        stk[top = 1] = x; for (int i = x; !isroot(i); i = fa[i]) stk[++top] = fa[i];
        while (top) pushdown(stk[top--]);
        while (!isroot(x)) {
            int y = fa[x], z = fa[y];
            if (!isroot(y))
                if (chk(x) == chk(y)) rotate(y);
                else rotate(x);
            rotate(x);
        }
    }
    void access(int x) { for (int y = 0; x; x = fa[y = x]) splay(x), ch[x][1] = y, pushup(x); }
    int findroot(int x) { access(x); splay(x); pushdown(x);
        while (ch[x][0])  x = ch[x][0], pushdown(x);
        splay(x); return x;
    }
    void makeroot(int x) { access(x); splay(x); swap(ch[x][0], ch[x][1]); rev[x] ^= 1; }
    void split(int x, int y) { makeroot(x); access(y); splay(y); }
    void link(int x, int y) { makeroot(x); fa[x] = y; }
    void cut(int x, int y) { split(x, y); fa[x] = ch[y][0] = 0; }
}
using namespace LCT;
int ans[N];
int main() {
#ifndef ONLINE_JUDGE
    file("P4172");
#endif
    int Q;
    n = rint(), m = rint(), Q = rint();
    For (i, 1, m) {
        edge[i].x = rint(), edge[i].y = rint(), edge[i].z = rint();
        if (edge[i].x > edge[i].y) swap(edge[i].x, edge[i].y);
    }
    sort(edge + 1, edge + 1 + m, cmp);
    For (i, 1, m) Id[mp(edge[i].x, edge[i].y)] = i;
    For (i, 1, Q) {
        q[i].op = rint(), q[i].x = rint(), q[i].y = rint();
        if (q[i].x > q[i].y) swap(q[i].x, q[i].y);
        if (q[i].op == 2) {
            q[i].id = Id[mp(q[i].x, q[i].y)];
            del[q[i].id] = 1;
        }
    }
    For (i, n + 1, m + n)
        mx[i] = val[i] = i - n;

    int sum = 0;
    For (i, 1, m) {
        if (del[i]) continue;
        if (sum == n - 1) break;
        int x = edge[i].x, y = edge[i].y;
        if (findroot(x) != findroot(y)) {
            link(x, i + n); link(i + n, y);
            sum ++;
        }
    }

    Forr (i, Q, 1) {
        int x = q[i].x, y = q[i].y;
        if (q[i].op == 1) {
            split(x, y); ans[i] = edge[mx[y]].z;
        } else {
            split(x, y);
            int t = mx[y];
            if (edge[q[i].id].z < edge[t].z) {
                cut(edge[t].x, t + n); cut(t + n, edge[t].y);
                link(x, q[i].id + n); link(y, q[i].id + n);
            }
        }
    }
    For (i, 1, Q) if (q[i].op == 1) printf("%d\n", ans[i]);
}

原文地址:https://www.cnblogs.com/HNYLMSTea/p/10458407.html

时间: 2024-10-14 23:10:57

P4172 [WC2006]水管局长的相关文章

洛谷P4172 [WC2006]水管局长 (LCT,最小生成树)

思路分析 在一个图中,要求路径上最大边边权最小,就不难想到最小生成树.而题目中有删边的操作,那肯定是要动态维护啦.直接上LCT维护边权最小值(可以参考一下蒟蒻的Blog) 这时候令人头疼的问题又冒出来了......删掉一条边以后,又不好从树断开后的两边选出最小的边在连上.这是根本维护不了的. 于是蒟蒻又get到了一个新套路--顺序解决不了的问题,可以离线询问,反过来处理.原来的删边变成了加边,就很方便了.直接split找出环上的最大边,当前要加的边比它小就替换掉. 一个做法的问题:在反过来初始化

[bzoj2594][Wc2006]水管局长数据加强版

论蒟蒻的自我修养T_T.. 和noi2014魔法森林基本一样...然而数据范围大得sxbk...100w你告诉我(n+m)log(n+m)可过?[掀桌] 蒟蒻又蠢了..复杂度应该是O((n+q)log(n+m))吧.. 一开始数组开太小re了两发(要开到maxn+maxm),然后又开太大mle一发,然后无限tle...把记录类型全改成数组还是tle.... 最后把非lct部分改得和黄学长全部一样终于20+s卡过去了......... 然后发现自己原来是有个地方写萎了..一开始把没被删的边做kru

bzoj 2594: [Wc2006]水管局长数据加强版 动态树

2594: [Wc2006]水管局长数据加强版 Time Limit: 25 Sec  Memory Limit: 128 MBSubmit: 934  Solved: 291[Submit][Status] Description SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径

【BZOJ 2594】 [Wc2006]水管局长数据加强版

2594: [Wc2006]水管局长数据加强版 Time Limit: 25 Sec  Memory Limit: 128 MB Submit: 1138  Solved: 364 [Submit][Status] Description SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等

BZOJ 2594: [Wc2006]水管局长数据加强版(kruskal + LCT)

Description SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了.嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项. 在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗.消毒等等.嘟嘟在

BZOJ_2594_[Wc2006]水管局长数据加强版_LCT

Description SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了.嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项. 在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗.消毒等等.嘟嘟在

[BZOJ2594] [Wc2006]水管局长数据加强版(LCT + kruskal + 离线)

传送门 WC这个题真是丧心病狂啊,就是想学习一下怎么处理边权,给我来了这么一个破题! ORZ hzwer 临摹黄学长代码233 但还是复杂的一匹 理一下思路吧 题目大意:给定一个无向图,多次删除图中的某一条边,求两点间路径最大值的最小值 求两点间的路径最大值的最小值的话,可以求最小生成树,那么这个值就是最小生成树上两点间路径上的最大值 但是题目要求是删除边,LCT维护最小生成树不支持删边操作,那么就离线处理,倒着加边,用LCT维护. 就是这个离线处理是最恶心的. 来说说如何处理边权,把边也抽象成

luoguP4172 [WC2006]水管局长

https://www.luogu.org/problemnew/show/P4172 考虑倒序处理所有操作 先把不会被删掉的边加入图中,LCT 维护最小生成树,再倒序插入每一条边,如果边的 ( l, r ) 在同一个联通块且( l, r ) 之间简单路径最大值大于这条边的权值则删掉 ( l, r ) 之间简单路径最大值这条边,加入 ( l, r ),不在则直接 link,查询时 ans 就是两点间边最大值 #include <bits/stdc++.h> using namespace st

【bzoj2594】[Wc2006]水管局长数据加强版

真是神题 当时调了几天没调出来 后来没管了 当时把fread去掉就TLE,加上就RE 一直在底下跟网上的程序拍,尝试各种优化常数都没用 拍出几组不一样的,发现我是对的,醉了,网上那个是怎么过的 记一下这蛋疼的代码 1 #include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cstdio> 6 #include<ma