bzoj2333: [SCOI2011]棘手的操作

1 篇文章 0 订阅
1 篇文章 0 订阅

题目链接
离线所有的操作,依次加边,并查集维护连通性,链表维护每个联通块的节点,每次合并时将两个链表首位相接,以此保证任何一个曾经存在过的连通块都是链表上连续的一段,这样我们就可以用线段树来维护连通块的修改和查询操作了。

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
 
const int N = 300030, inf = 0x3f3f3f3f;
 
char buf;
inline int read() {
    int f = 1, rst = 0;
    do buf = getchar(); while (! isdigit(buf) and buf != '-');
    if (buf == '-') f = -1, buf = getchar();
    while (isdigit(buf)) rst = rst*10 + buf - '0', buf = getchar();
    return rst*f;
}
 
struct query {
    int kd, x, v;
    query() {}
    query(int __kd, int __x, int __v): kd(__kd), x(__x), v(__v) {
//      printf("%d %d %d\n", kd, x, v);
    }
}lib[N];
 
int n, q, a[N], fa[N], nxt[N], ed[N], tot, id[N], val[N];
char str[10];
int maxv[N<<1], gl, gr, gx, mark[N<<1];
#define g(l, r) (l + r | l != r)
#define o g(l, r)
#define ls g(l, mid)
#define rs g(mid + 1, r)
#define set(l, r, x) do {gl = l, gr = r, gx = x;} while(0)
 
int find(int x) {
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}
 
inline void update(int l, int r) {
    if (l == r) return;
    int mid = l + r >> 1;
    maxv[o] = max(maxv[ls], maxv[rs]);
}
 
void build(int l, int r) {
    if (l == r) {
        maxv[o] = val[l]; return;
    } int mid = l + r >> 1;
    build(l, mid); build(mid + 1, r);
    update(l, r);
}
 
inline void init() {
    for (int i = 1; i <= n; i++) if (find(i) == i)
        for (int j = i; j; j = nxt[j]) id[j] = ++tot, val[tot] = a[j];
    for (int i = 1; i <= n; i++) fa[i] = ed[i] = i;
    build(1, n);
}
 
inline void push(int l, int r) {
    if (! mark[o] or l == r) return;
    int mid = l + r >> 1;
    mark[ls] += mark[o]; maxv[ls] += mark[o];
    mark[rs] += mark[o]; maxv[rs] += mark[o];
    mark[o] = 0;
}
 
inline void Change(int l, int r) {
    push(l, r);
    if (gl <= l and r <= gr) {
        mark[o] += gx; maxv[o] += gx; push(l, r); return;
    } int mid = l + r >> 1;
    if (gl <= mid) Change(l, mid);
    if (gr >= mid + 1) Change(mid + 1, r);
    update(l, r);
}
 
inline void change(int l, int r, int x) {
    set(l, r, x); Change(1, n);
}
 
inline void GetMax(int l, int r) {
    push(l, r);
    if (gl <= l and r <= gr) {
        gx = max(gx, maxv[o]); return;
    } int mid = l + r >> 1;
    if (gl <= mid) GetMax(l, mid);
    if (gr >= mid + 1) GetMax(mid + 1, r);
//  update(l, r);
}
 
inline int getMax(int l, int r) {
    set(l, r, -inf); GetMax(1, n); return gx;
}
 
int main() {
    n = read(); memset(maxv, 0xd0, sizeof(maxv));
    for (int i = 1; i <= n; i++) a[i] = read(), ed[i] = fa[i] = i;
    q = read();
    for (int i = 1; i <= q; i++) {
        scanf("%s", str);
        if (*str == 'U') {
            int x = read(), y = read();
            lib[i] = query(0, x, y);
            int r1 = find(x), r2 = find(y);
            if (r1 == r2) continue;
            fa[r2] = r1; nxt[ed[r1]] = r2; ed[r1] = ed[r2];
        } else if (*str == 'A') {
            if (str[1] == '1') {
                int x = read(), v = read();
                lib[i] = query(1, x, v);
            } else if (str[1] == '2') {
                int x = read(), v = read();
                lib[i] = query(2, x, v);
            } else lib[i] = query(3, 0, read());
        } else {
            if (str[1] == '1') lib[i] = query(4, read(), 0);
            else if (str[1] == '2') lib[i] = query(5, read(), 0);
            else lib[i] = query(6, 0, 0);
        }
    } init();
    for (int i = 1; i <= q; i++) {
        int kd = lib[i].kd, x = lib[i].x, v = lib[i].v;
        if (! kd) {
            int r1 = find(x), r2 = find(v);
            if (r1 == r2) continue;
            fa[r2] = r1; ed[r1] = ed[r2];
        } else if (kd == 1) change(id[x], id[x], v);
        else if (kd == 2) change(id[find(x)], id[ed[find(x)]], v);
        else if (kd == 3) change(1, n, v);
        else if (kd == 4) printf("%d\n", getMax(id[x], id[x]));
        else if (kd == 5) printf("%d\n", getMax(id[find(x)], id[ed[find(x)]]));
        else printf("%d\n", getMax(1, n));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值