BZOJ 3514 [LCT][主席树]

21 篇文章 0 订阅
10 篇文章 0 订阅

Description D e s c r i p t i o n

N N 个点M条边的无向图,询问保留图中编号在 [l,r] [ l , r ] 的边的时候图中的联通块个数。
强制在线。

Solution S o l u t i o n

考虑一个森林的连通图个数。
就是点数减边数。
按加入边的时间为边的权值,维护最大生成树。
加入一条边时,若已经联通,就 Cut C u t 掉权值最小的边。
记录一下每条边的 pre p r e 值:表示加入它时 Cut C u t 掉的边的权值。
若不连通则 pre p r e 0 0
这样对于一个区间[l,r]中的边来说,只有 pre p r e 值小于 l l <script type="math/tex" id="MathJax-Element-23">l</script>才会是树边。
这个东西用主席树维护一下就好啦。

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

const int N = 404040;
const int INF = 1 << 30;
typedef pair<int, int> P;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
inline void read(int &x) {
    static char c; x = 0;
    for (c = get(); c < '0' || c > '9'; c = get());
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
}

inline int Min(int a, int b) {
    return a < b ? a : b;
}

int n, m, k, x, y, p, ans;
P G[N];
int pre[N];
namespace UFS {
    int fa[N], rk[N];
    void Init(int n) {
        for (int i = 1; i <= n; i++) fa[i] = i;
    }
    inline int Fa(int x) {
        return fa[x] == x ? x : Fa(fa[x]);
    }
    inline int Merge(int x, int y) {
        static int f1, f2;
        f1 = Fa(x); f2 = Fa(y);
        if (f1 == f2) return 0;
        if (rk[f1] < rk[f2]) swap(f1, f2);
        if (rk[f1] == rk[f2]) rk[f1]++;
        fa[f2] = f1; return 1;
    }
}
namespace Seg {
    int rt[N];
    int Tcnt;
    int ls[N * 40], rs[N * 40], sum[N * 40];
    inline void Modify(int &o, int l, int r, int pos, int x) {
        ls[++Tcnt] = ls[o]; rs[Tcnt] = rs[o];
        sum[Tcnt] = sum[o]; o = Tcnt;
        if (l == r) return (void)(++sum[o]);
        int mid = (l + r) >> 1;
        if (pos <= mid) Modify(ls[o], l, mid, pos, x);
        else Modify(rs[o], mid + 1, r, pos, x);
        sum[o] = sum[ls[o]] + sum[rs[o]];
    }
    inline int Sum(int o, int l, int r, int L, int R) {
        if (!o) return 0;
        if (l >= L && r <= R) return sum[o];
        int mid = (l + r) >> 1, res = 0;
        if (L <= mid) res += Sum(ls[o], l, mid, L, R);
        if (R > mid) res += Sum(rs[o], mid + 1, r, L, R);
        return res;
    }
    inline void Add(int i, int res, int x) {
        Modify(rt[i], 1, n + m, res, x);
    }
    inline int Query(int l, int r, int L, int R) {
        return Sum(rt[r], 1, n + m, L, R) - Sum(rt[l - 1], 1, n + m, L, R);
    }
}
namespace LCT {
    struct node {
        node *ch[2];
        node *fa;
        int mn, id, rev;
        inline void PushUp(void) {
            mn = Min(ch[0]->mn, ch[1]->mn);
            mn = Min(id, mn);
        }
        inline void PushDown(void) {
            if (rev) {
                swap(ch[0], ch[1]);
                ch[0]->rev ^= 1;
                ch[1]->rev ^= 1;
                rev = 0;
            }
        }
    };
    node T[N];
    node *null;
    inline bool IsRoot(node* x) {
        return x->fa == null || (x->fa->ch[0] != x && x->fa->ch[1] != x);
    }
    inline void Init(int n) {
        null = T;
        null->ch[0] = null->ch[1] = null->fa = null;
        null->mn = INF;
        for (int i = 1; i <= n; i++) {
            T[i].ch[0] = T[i].ch[1] = T[i].fa = null;
            T[i].id = T[i].mn = INF; T[i].rev = 0;
        }
    }
    inline void Rotate(node* x) {
        node *y = x->fa, *z = y->fa;
        int l = (y->ch[0] != x), r = l ^ 1;
        if (!IsRoot(y)) {
            if (z->ch[0] == y) z->ch[0] = x;
            else z->ch[1] = x;
        }
        x->fa = z; y->fa = x; x->ch[r]->fa = y;
        y->ch[l] = x->ch[r]; x->ch[r] = y;
        y->PushUp(); x->PushUp();
    }
    inline void Down(node* x) {
        if (!IsRoot(x)) Down(x->fa);
        x->PushDown();
    }
    inline void Splay(node* x) {
        Down(x);
        while (!IsRoot(x)) {
            node *y = x->fa, *z = y->fa;
            if (!IsRoot(y)) {
                if (y->ch[0] == x ^ z->ch[0] == y) Rotate(x);
                else Rotate(y);
            }
            Rotate(x);
        }
    }
    inline void Access(node* x) {
        for (node *y = null; x != null; x = x->fa) {
            Splay(x); x->ch[1] = y;
            x->PushUp(); y = x;
        }
    }
    inline void MakeRoot(node* x) {
        Access(x); Splay(x); x->rev ^= 1;
    }
    inline void Link(node* x, node* y) {
        MakeRoot(x); x->fa = y;
    }
    inline void Cut(node* x, node* y) {
        MakeRoot(x); Access(y); Splay(y);
        x->fa = y->ch[0] = null; y->PushUp();
    }
    inline int Query(node* x, node* y) {
        MakeRoot(x); Access(y); Splay(y);
        return y->mn;
    }
    inline void AddEdge(int x, int y, int pos) {
        Seg::rt[pos] = Seg::rt[pos - 1];
        if (x == y) return;
        G[n + pos] = P(x, y);
        T[n + pos].id = T[n + pos].mn = n + pos;
        int res = n;
        if (!UFS::Merge(x, y)) {
            res = Query(T + x, T + y);
            Cut(T + res, T + G[res].first);
            Cut(T + res, T + G[res].second);
        }
        pre[pos] = res;
        Seg::Add(pos, res, 1);
        Link(T + n + pos, T + x);
        Link(T + n + pos, T + y);
    }
}


int main(void) {
    read(n); read(m); read(k); read(p);
    LCT::Init(n + m); UFS::Init(n);
    for (int i = 1; i <= m; i++) { 
        read(x); read(y);
        LCT::AddEdge(x, y, i);
    }
    for (int i = 1; i <= k; i++) {
        read(x); read(y);
        if (p) {
            x ^= ans; y ^= ans;
        }
        printf("%d\n", ans = n - Seg::Query(x, y, n, n + x - 1));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值