LGOJ4172 WC2006水管局长

拥有回忆 提交于 2020-02-26 09:09:10

首先声明,这份代码空间复杂度 \(O(n^2)\),瓶颈在给边打标记

由于博主太菜,懒得再改成低复杂度的打标记了,所以\(BZOJ\)的数据过不去

Description

link

给一张图,会有删边操作,求当前图路径\((x,y)\) 的最大边权

\(n \leq 10^3 ,m \le 10^5\)

\(BZOJ\)版:\(n\le 10^5,m\le10^6\)

Solution

\[Begin\]

明确两点:

1.这个题要\(LCT\)(因为有删边的操作)

2.这个题把询问离线下来,然后逆序加边更可做

这个题中LCT维护的是“边权”

这里有一个点边转化:

把每一个边拆成点,对端点连两条边(边权就是\(e[i].dis\)),这样就可以维护点权了,在\(link\)\(cut\)操作中有点点不同

如下:

cut(e[tmp - n].from, tmp);
cut(e[tmp - n].to, tmp);
link(x[i], id[x[i]][y[i]] + n);
link(y[i], id[x[i]][y[i]] + n);

先给出图的最终态,然后求一个最小生成树

每个操作先打通链,也就是 \(split\)

然后对于询问操作,直接给\(s[y]\)

对于改边的操作

每一次加边,都会在生成树上面添加一个环,然后我们找到环上最大边 \(cut\) 就可以

\(split\)已经通链了,所以直接断就可以)

然后\(link\)就行了

\[Q.E.D\]

Code

不是很难写吧

#include <bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm {
inline int read() {
    int res = 0, f = 1;
    char k;
    while (!isdigit(k = getchar()))
        if (k == '-')
            f = -1;
    while (isdigit(k)) res = res * 10 + k - '0', k = getchar();
    return res * f;
}
const int M = 1e6 + 10, N = 1e5 + 10;
int f[N], c[N][2], s[N], v[N], st[N], n, m, q, x[N], y[N], opt[N], fa[N], ans[N], id[2010][2010];
bool r[N];
struct node {
    int from, to, dis, vis;
    bool operator<(const node &a) const { return dis < a.dis; }
} e[M];
inline void push_up(int x) {
    s[x] = max(s[c[x][0]], max(s[c[x][1]], v[x]));
    return;
}
inline bool notroot(int x) { return c[f[x]][1] == x || c[f[x]][0] == x; }
inline void pushr(int x) {
    swap(c[x][0], c[x][1]);
    r[x] ^= 1;
    return;
}
inline void push_down(int x) {
    if (r[x]) {
        if (c[x][0])
            pushr(c[x][0]);
        if (c[x][1])
            pushr(c[x][1]);
    }
    return r[x] = 0, void();
}
inline void rotate(int tx) {
    int ty = f[tx], z = f[ty], k = (c[ty][1] == tx), w = c[tx][!k];
    if (notroot(ty))
        c[z][c[z][1] == ty] = tx;
    c[tx][!k] = ty;
    c[ty][k] = w;
    if (w)
        f[w] = ty;
    f[ty] = tx;
    f[tx] = z;
    return push_up(ty), push_up(tx);
}
inline void splay(int x) {
    int y = x, z = 0;
    st[++z] = y;
    while (notroot(y)) st[++z] = y = f[y];
    while (z) push_down(st[z--]);
    while (notroot(x)) {
        y = f[x], z = f[y];
        if (notroot(y))
            rotate((c[y][0] == x) ^ (c[z][0] == y) ? x : y);
        rotate(x);
    }
    return push_up(y);
}
inline void access(int x) {
    for (int y = 0; x; x = f[y = x]) splay(x), c[x][1] = y, push_up(x);
    return;
}
inline void makeroot(int x) {
    access(x);
    splay(x);
    pushr(x);
    return;
}
inline int findroot(int x) {
    access(x);
    splay(x);
    while (c[x][0]) push_down(x), x = c[x][0];
    splay(x);
    return x;
}
inline void split(int x, int y) {
    makeroot(x);
    access(y);
    splay(y);
    return;
}
inline void link(int x, int y) {
    makeroot(x);
    if (findroot(y) != x)
        f[x] = y;
    return;
}
inline void cut(int x, int y) {
    makeroot(x);
    if (findroot(y) == x && f[y] == x && !c[y][0])
        f[y] = c[x][1] = 0, push_up(x);
    return;
}

inline int find(int x, int val) {
    if (v[x] == val)
        return x;
    return s[c[x][0]] == val ? find(c[x][0], val) : find(c[x][1], val);
}

inline int getf(int x) { return x == fa[x] ? x : fa[x] = getf(fa[x]); }
inline void merge(int x, int y) { fa[getf(x)] = getf(y); }
inline void Kruskal() {
    for (int i = 1; i <= n; ++i) fa[i] = i;
    for (int i = 1; i <= m; ++i) {
        if (!e[i].vis && getf(e[i].from) != getf(e[i].to)) {
            merge(e[i].from, e[i].to);
            link(e[i].from, n + i);
            link(e[i].to, n + i);
        }
    }
    return;
}

signed main() {
    n = read();
    m = read();
    q = read();
    for (int i = 1; i <= m; ++i) e[i].from = read(), e[i].to = read(), e[i].dis = read();
    sort(e + 1, e + m + 1);
    for (int i = 1; i <= m; ++i) {
        id[e[i].from][e[i].to] = id[e[i].to][e[i].from] = i;
        v[n + i] = e[i].dis;
    }
    for (int i = 1; i <= q; ++i) {
        opt[i] = read();
        x[i] = read();
        y[i] = read();
        if (opt[i] == 2)
            e[id[x[i]][y[i]]].vis = 1;
    }
    Kruskal();
    int num = 0;
    for (int i = q; i >= 1; --i) {
        split(x[i], y[i]);
        if (opt[i] == 1)
            ans[++num] = s[y[i]];
        else {
            int tmp = find(y[i], s[y[i]]);
            if (v[id[x[i]][y[i]] + n] < s[y[i]]) {
                cut(e[tmp - n].from, tmp);
                cut(e[tmp - n].to, tmp);
                link(x[i], id[x[i]][y[i]] + n);
                link(y[i], id[x[i]][y[i]] + n);
            }
        }
    }
    for (int i = num; i >= 1; --i) printf("%lld\n", ans[i]);
    return 0;
}
}  // namespace yspm
signed main() { return yspm::main(); }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!