[BZOJ2816][ZJOI2012]网络(LCT)

如果对LCT(Link-Cut Tree)不了解,请参考链接http://www.cnblogs.com/BLADEVIL/p/3510997.html
对于每一种颜色,维护一个 LCT (因为没有同色的环,且 C<=10 )。每种颜色对应的 LCT 中包含所有的点,但只包含对应颜色的边。
对于操作 0 ,就是修改点权,也就是Splay上的操作,就不多说了(要注意一点,每一种颜色对应的 LCT 上的节点 x 都要修改)。
对于操作2,就是找出对应颜色的 LCT ,提取路径并询问路径最大值,这也不多说了。
而重点就是操作 1
对于判断一条边是否存在,可以使用map来判定,当然暴力枚举也可以(因为由限制条件1可得,与一个点相连的边数不超过 2C )。
在这里我不知道数据中会不会出现「一条边修改成这条边当前的颜色」的情况,所以我判定了一下,如果出现这种情况就直接判定为同时满足限制条件 1 2 continue
对于判断是否满足限制条件 1 ,可以使用统计的方法,即统计与每个点相连的每种颜色的边有几条,如果节点u v 中至少有一个点相连的颜色为w的边数等于 2 ,那么限制条件1不满足。
对于限制条件 2 ,直接判断在颜色w对应的 LCT 中,是否 FindRoot(u)==FindRoot(v) ,如果是则限制条件 2 不满足。
如果同时满足限制条件1 2 ,则维护一下统计数组,然后在这条边原来的颜色对应的LCT中,执行 Cut(u,v) ,再在颜色 w 对应的LCT中,执行 Link(u,v)
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 1e4 + 5, M = 1e5 + 5;
int n, m, fa[M], lc[M], rc[M], rev[M], len, que[M], c, Q,
val[M], V[M], cnt[N][13], ecnt = 1, nxt[M << 1], adj[M], go[M << 1],
col[M << 1];
void add_edge(int u, int v, int w) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt;
    go[ecnt] = v; col[ecnt] = w;
}
int tran(int x, int y) {return (x - 1) * c + y;}
int which(int x) {return rc[fa[x]] == x;}
bool is_root(int x) {
    return !fa[x] || (lc[fa[x]] != x && rc[fa[x]] != x);
}
void down(int x) {
    if (rev[x]) {
        swap(lc[x], rc[x]);
        if (lc[x]) rev[lc[x]] ^= 1;
        if (rc[x]) rev[rc[x]] ^= 1;
        rev[x] = 0;
    }
}
void upt(int x) {
    val[x] = V[x];
    if (lc[x]) val[x] = max(val[x], val[lc[x]]);
    if (rc[x]) val[x] = max(val[x], val[rc[x]]);
}
void rotate(int x) {
    int y = fa[x], z = fa[y], b = lc[y] == x ? rc[x] : lc[x];
    if (z && !is_root(y)) (lc[z] == y ? lc[z] : rc[z]) = x;
    fa[x] = z; fa[y] = x; b ? fa[b] = y : 0;
    if (lc[y] == x) rc[x] = y, lc[y] = b;
    else lc[x] = y, rc[y] = b; upt(y); upt(x);
}
void splay(int x) {
    int i, y; que[len = 1] = x;
    for (y = x; !is_root(y); y = fa[y]) que[++len] = fa[y];
    for (i = len; i >= 1; i--) down(que[i]);
    while (!is_root(x)) {
        if (!is_root(fa[x])) {
            if (which(x) == which(fa[x])) rotate(fa[x]);
            else rotate(x);
        }
        rotate(x);
    }
    upt(x);
}
void Access(int x) {
    int y;
    for (y = 0; x; y = x, x = fa[x]) {
        splay(x); rc[x] = y;
        if (y) fa[y] = x; upt(x);
    }
}
int Find_Root(int x) {
    Access(x); splay(x);
    while (down(x), lc[x]) x = lc[x];
    splay(x); return x;
}
void Make_Root(int x) {
    Access(x); splay(x);
    rev[x] ^= 1;
}
void Link(int x, int y) {
    Make_Root(x); fa[x] = y;
}
void Cut(int x, int y) {
    Make_Root(x); Access(y); splay(y);
    lc[y] = 0; fa[x] = 0; upt(y);
}
int Select(int x, int y) {
    Make_Root(x); Access(y); splay(y);
    return val[y];
}
int main() {
    int i, j, op, x, y, z;
    n = read(); m = read(); c = read(); Q = read();
    for (i = 1; i <= n; i++) {
        x = read(); for (j = 1; j <= c; j++) {
            int p = tran(i, j);
            V[p] = val[p] = x;
        }
    }
    while (m--) {
        x = read(); y = read(); z = read() + 1;
        cnt[x][z]++; cnt[y][z]++;
        Link(tran(x, z), tran(y, z));
        add_edge(x, y, z); add_edge(y, x, z);
    }
    while (Q--) {
        op = read(); x = read(); y = read();
        if (op == 0) for (i = 1; i <= c; i++)
            z = tran(x, i), splay(z), V[z] = y, upt(z);
        else if (op == 1) {
            z = read() + 1; bool flag = 0; int co, ex;
            for (int e = adj[x]; e; e = nxt[e])
                if (go[e] == y)
                    {ex = e; co = col[e]; flag = 1; break;}
            if (!flag) {printf("No such edge.\n"); continue;}
            if (co == z) {printf("Success.\n"); continue;}
            if (cnt[x][z] == 2 || cnt[y][z] == 2)
                {printf("Error 1.\n"); continue;}
            if (Find_Root(tran(x, z)) == Find_Root(tran(y, z)))
                {printf("Error 2.\n"); continue;}
            printf("Success.\n"); Cut(tran(x, co), tran(y, co));
            Link(tran(x, z), tran(y, z));
            col[ex] = col[ex ^ 1] = z; cnt[x][co]--; cnt[y][co]--;
            cnt[x][z]++; cnt[y][z]++;
        }
        else {
            x++; z = read();
            if (Find_Root(tran(y, x)) != Find_Root(tran(z, x)))
                {printf("-1\n"); continue;}
            else printf("%d\n", Select(tran(y, x), tran(z, x)));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值