[BZOJ3514]Codechef MARCH14 GERALD07加强版

4 篇文章 0 订阅
3 篇文章 0 订阅

题目链接BZOJ3514

分析
1. 首先,对于询问 [l..r] ,我们用 [1..r] 的边,设边权为边的标号,建立一颗最大生成树;那么,点数减去树上的编号为 [l..r] 的边的数量即为答案。
2. 那么,依次加边,LCT维护最大生成树,用主席树记录加完 i 这条边后,有哪些边还在树上。
3. 询问时直接询问第r棵线段树的 [l..r] 的边的数量 x nx即为答案。

上代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;

int n, m, q, type;
inline int read() {
    char ch = getchar();
    register int ans = 0, neg = 1;
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-') neg = -1;
    for (; isdigit(ch); ch = getchar())
        ans = ans * 10 + ch - '0';
    return ans * neg;
}

namespace LCT {
    const int M = 4e5 + 10;

    int cnt;
    bool rev[M];
    int fa[M], ch[M][2], val[M], minp[M];
    #define lc(a) (ch[a][0])
    #define rc(a) (ch[a][1])
    #define csk(a) (rc(fa[a]) == a)
    #define makeLink(a, b, c) (fa[ch[a][c] = b] = a)
    #define isRoot(a) (lc(fa[a]) != a && rc(fa[a]) != a)
    void init() {
        cnt = n;
        for (int i = 0; i <= n; ++i)
            val[i] = INF, minp[i] = i;
    }
    inline void update(int a) {
        minp[a] = a;
        if (val[minp[lc(a)]] < val[minp[a]]) minp[a] = minp[lc(a)];
        if (val[minp[rc(a)]] < val[minp[a]]) minp[a] = minp[rc(a)];
    }
    inline void reverse(int a) {
        rev[a] ^= 1, swap(lc(a), rc(a));
    }
    inline void pushDown(int a) {
        if (rev[a]) {
            rev[a] = false;
            reverse(lc(a)), reverse(rc(a));
        }
    }
    void pushDownTo(int a) {
        if (!isRoot(a))
            pushDownTo(fa[a]);
        pushDown(a);
    }
    inline void rotate(int a) {
        int c = csk(a), f = fa[a];
        makeLink(f, ch[a][c ^ 1], c);
        if (isRoot(f)) fa[a] = fa[f];
        else makeLink(fa[f], a, csk(f));
        makeLink(a, f, c ^ 1);
        update(f), update(a);
    }
    inline void splay(int a) {
        pushDownTo(a);
        while (!isRoot(a)) {
            if (!isRoot(fa[a])) {
                if (csk(a) == csk(fa[a])) rotate(fa[a]);
                else rotate(a);
            } rotate(a);
        }
    }
    inline void access(int a) {
        splay(a), rc(a) = 0, update(a);
        for (int i = fa[a]; i; i = fa[a = i])
            splay(i), rc(i) = a, update(i);
    }
    inline void makeRoot(int a) {
        access(a), splay(a), reverse(a);
    }
    inline void link(int a, int b) {
        makeRoot(a), fa[a] = b;
    }
    inline void cut(int a, int b) {
        makeRoot(a), access(b), splay(b);
        lc(b) = fa[a] = 0, update(b);
    }
    inline int getMin(int a, int b) {
        return makeRoot(a), access(b), splay(b), val[minp[b]];
    }
    inline void delMin(int a, int b) {
        makeRoot(a), access(b), splay(b);
        int tmp = minp[b];
        cut(a, tmp), cut(b, tmp);
    }
    inline void addLink(int a, int b, int c) {
        int tmp = ++cnt; val[tmp] = c;
        update(tmp), link(a, tmp), link(b, tmp);
    }
}

namespace CT {
    const int M = 75e5 + 10;

    int T[N], cnt;
    int lc[M], rc[M], tot[M];
    int modify(int a, int l, int r, int p, int c) {
        int now = ++cnt; tot[now] = tot[a] + c;
        if (l == r) return now;
        int mid = (l + r) >> 1;
        if (p <= mid) rc[now] = rc[a], lc[now] = modify(lc[a], l, mid, p, c);
        else lc[now] = lc[a], rc[now] = modify(rc[a], mid + 1, r, p, c);
        return now;
    }
    int query(int a, int l, int r, int ll, int rr) {
        if (ll > rr) return 0;
        if (l == ll && r == rr) return tot[a];
        int mid = (l + r) >> 1;
        if (rr <= mid) return query(lc[a], l, mid, ll, rr);
        else if (ll > mid) return query(rc[a], mid + 1, r, ll, rr);
        return query(lc[a], l, mid, ll, mid) + query(rc[a], mid + 1, r, mid + 1, rr);
    }
}

int fa[N];
int findRoot(int a) {
    return fa[a] == a ? a : fa[a] = findRoot(fa[a]);
}
int main() {
    n = read(), m = read(), q = read(), type = read();
    LCT::init();
    for (int i = 1; i <= n; ++i) fa[i] = i;
    for (int i = 1; i <= m; ++i) {
        int a = read(), b = read();
        CT::T[i] = CT::T[i - 1];
        if (a == b) continue;
        if (findRoot(a) == findRoot(b)) {
            CT::T[i] = CT::modify(CT::T[i], 1, m, i, 1);
            CT::T[i] = CT::modify(CT::T[i], 1, m, LCT::getMin(a, b), -1);
            LCT::delMin(a, b), LCT::addLink(a, b, i);
        } else {
            CT::T[i] = CT::modify(CT::T[i], 1, m, i, 1);
            LCT::addLink(a, b, i), fa[findRoot(a)] = findRoot(b);
        }
    }
    int ans = 0;
    for (int i = 1; i <= q; ++i) {
        int l = read(), r = read();
        l = l ^ (ans * type), r = r ^ (ans * type);
        printf("%d\n", ans = n - CT::query(CT::T[r], 1, m, l , r));
    }
    return 0;
}

以上

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值