打打打打打字机|

realFish

园龄:3年1个月粉丝:3关注:0

BZOJ3514 GERALD07 题解

题面

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

题解

首先考虑暴力。从 lr 枚举每条边,若当前两点已连通,则不计数;否则将连通块数量-1。另一种方法是迭代地统计连接两不连通点的边出现次数 s,则答案为 Ns。用并查集维护即可。
考虑对其优化。从 1M 枚举每条边 i,若当前两点已连通,则删去环上出现时间最早的边,并记录其出现的时间为 ti。否则 ti=0。则 slrt小于 l 的数的个数。
证明。在环上,删除出现时间为 ti 的边即可重新形成树结构,若 ti<l,则从 l 开始加边时一定不会加入 ti 边。相当于 s++。否则 s 显然不能加。证毕。
以上操作可以用LCT实现。

之后,问题只剩下求 [l,r] 区间内小于 l 的数的个数。而此题强制在线,那么要用树套树/主席树实现。
但本蒟蒻还没学主席树,且树套树空间开不下……于是只能用离线的二维偏序拿部分分了。

Code

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2e5 + 5;
int n, m, mq, type, ans[N];
int fa[N], v[N << 1], stack[N << 1], top;
vector<int> r[N];
struct Tree {int l, r, sum;} tr[N << 2];
struct Edge {int x, y;} e[N];
struct Node {int s[2], p, dat, rev;} t[N << 1];
struct Qry {
    int id, l, r;
    bool operator <(const Qry &o) const {
        return l < o.l;
    }
} q[N];
int read() {
    int x = 0; char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}
    return x;
}
int get(int x) {
    if (x == fa[x]) return x;
    return fa[x] = get(fa[x]);
}
bool isroot(int x) {
    return t[t[x].p].s[0] != x && t[t[x].p].s[1] != x;
}
void pushup(int x) {
    t[x].dat = x;
    for (int i = 0; i < 2; i++)
        if (t[x].s[i] && v[t[t[x].s[i]].dat] < v[t[x].dat])
            t[x].dat = t[t[x].s[i]].dat;
}
void pushrev(int x) {
    swap(t[x].s[0], t[x].s[1]); t[x].rev ^= 1;
}
void pushdown(int x) {
    if (!t[x].rev) return ;
    pushrev(t[x].s[0]); pushrev(t[x].s[1]); t[x].rev = 0;
}
void rotate(int x) {
    int y = t[x].p, z = t[y].p, k = t[y].s[1] == x;
    if (!isroot(y)) t[z].s[t[z].s[1] == y] = x; t[x].p = z;
    t[y].s[k] = t[x].s[k ^ 1]; t[t[x].s[k ^ 1]].p = y;
    t[x].s[k ^ 1] = y; t[y].p = x;
    pushup(y); pushup(x);
}
void splay(int x) {
    int p = x;
    stack[++top] = p;
    while (!isroot(p)) stack[++top] = p = t[p].p;
    while (top) pushdown(stack[top--]);
    while (!isroot(x)) {
        int y = t[x].p, z = t[y].p;
        if (!isroot(y))
            if (t[z].s[1] == y ^ t[y].s[1] == x) rotate(x);
            else rotate(y);
        rotate(x);
    }
}
void access(int x) {
    int z = x;
    for (int y = 0; x; y = x, x = t[x].p) {
        splay(x); t[x].s[1] = y; pushup(x);
    }
    splay(z);
}
void makeroot(int x) {
    access(x); pushrev(x);
}
int findroot(int x) {
    access(x);
    while (t[x].s[0]) {
        pushdown(x); x = t[x].s[0];
    }
    splay(x);
    return x;
}
void split(int x, int y) {
    makeroot(x); access(y);
}
void link(int x, int y) {
    makeroot(x); t[x].p = y;
}
void cut(int x, int y) {
    makeroot(x);
    if (findroot(y) == x && t[x].s[1] == y && !t[y].s[0]) {
        t[x].s[1] = t[y].p = 0; pushup(x);
    }
}
void Build(int l, int r, int x) {
    tr[x].l = l; tr[x].r = r;
    if (l == r) return ;
    int mid = l + r >> 1;
    Build(l, mid, x << 1); Build(mid + 1, r, x << 1 | 1);
}
void Insert(int p, int x) {
    tr[x].sum++;
    if (tr[x].l == tr[x].r) return ;
    int mid = tr[x].l + tr[x].r >> 1;
    if (p <= mid) Insert(p, x << 1);
    else Insert(p, x << 1 | 1);
}
int Query(int l, int r, int x) {
    if (l <= tr[x].l && tr[x].r <= r) return tr[x].sum;
    int mid = tr[x].l + tr[x].r >> 1, res = 0;
    if (l <= mid) res += Query(l, r, x << 1);
    if (r > mid) res += Query(l, r, x << 1 | 1);
    return res;
}
int main() {
    n = read(); m = read(); mq = read(); type = read();
    for (int i = 1; i <= n; i++) {
        v[i] = 0x7fffffff; fa[i] = i;
    }
    for (int i = 1; i <= n + m; i++) t[i].dat = i;
    for (int i = 1; i <= m; i++) {
        int x = read(), y = read();
        e[i] = (Edge){x, y};
        v[i + n] = i;
        if (x == y) {
            r[i].push_back(i); continue;
        }
        if (get(x) != get(y)) {
            link(x, i + n); link(y, i + n); fa[get(x)] = get(y); r[0].push_back(i);
        }
        else {
            split(x, y);
            int p = t[y].dat;
            r[v[p]].push_back(i);
            cut(e[p - n].x, p); cut(e[p - n].y, p);
            link(x, i + n); link(y, i + n);
        }
    }
    for (int i = 1; i <= mq; i++) q[i] = (Qry){i, read(), read()};
    sort(q + 1, q + mq + 1);
    Build(1, m, 1);
    for (int i = 0, j = 1; i <= m; i++) {
        while (j <= mq && q[j].l == i) {
            ans[q[j].id] = Query(q[j].l, q[j].r, 1); j++;
        }
        for (int j = 0; j < r[i].size(); j++) Insert(r[i][j], 1);
    }
    for (int i = 1; i <= mq; i++) printf("%d\n", n - ans[i]);
    return 0;
}

本文作者:realFish的博客

本文链接:https://www.cnblogs.com/fish07/p/16179210.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   realFish  阅读(22)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起