线段树分治

动态图联通性(可离线)

(loj121)给你一张无向图,你要支持如下操作:
1:删除一条边
2:加入一条边
3:查询某两个点对间是否联通

①在线做法:LCT

②离线做法:线段树分治

口胡做法:
把操作的顺序当做时间。
每条边维护一个存活区间,代表这条边在这个时间区间里面活着。
对时间轴建立一颗线段树,从线段树根开始dfs。
进入一个子树就把它这个子树中完全存活的边的两个点用按秩合并并查集合并。
一直到一个叶子的时候统计答案。
退出一个子树,或者到了一个叶子回去的时候。把这个子树内新加的这些存活的边删去。
(因为一条边只会出现一次,所以可以用一个数组来存之前出现过的边的标号)

注意点:
①:并查集合并之前要注意 f 1 ! = f 2 f1!=f2 f1!=f2,否则会有很多冗余操作,会TTT。
②:注意这东西是无向边。所以一开始读入的时候给边定个序就可以了。否则会删不了查不了。
③:如果一条边重复多次加入删除,可以在每次删除的时候把数组那一位清空就好了。

维护动态图的其它性质
bzoj4025 给一个动态图,问每一时刻是否是二分图。允许离线。

线段树分治就套一个板子,关键是怎么判断二分图。

我们知道,一个图是二分图当且仅当它没有奇环。考虑用并查集维护一个点到真正图的根的距离。 原因是我们getfa是在bcj的树上进行的,但是要建立起这个树和真正图的一个对应。考虑这样做那些重复计算的边恰好被异或掉了。画个图好些

如果一开始两边是联通的,那么只要判断是否会造成奇环就好了。用 g e t d i s getdis getdis一路异或上去就得到了两个点之间的真实距离的奇偶性。判断一下就好了。

如果一开始两边是不联通的,那么只有一个点的 d i s dis dis的奇偶性会改变,就是小堆的代表元。考虑其它路径上的点在异或的时候会全部抵消完。就没有了影响,所以只要修改小堆代表元的那个 d i s dis dis就行了。

这里有一个小优化,如果一个点为根的时候已经存在奇环了。那么它的子树里的点也一定存在了。所以不用递归下去了。

有一个小问题,因为这里点数是 1 e 5 1e5 1e5级别的,就不能用数组来存一个边是否存在。可以使用 u n o r d e r e d _ m a p unordered\_map unordered_map等数据结构维护。

实现起来有几个小细节:
①:数据是有可能 s t = e d st=ed st=ed的,这个时候是没有存活区间的,需要特判。不然RE
②:写 i f if if的时候要注意括号括起来。。。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#define ls (k<<1)
#define rs ((k<<1)|1)
using namespace std;
const int N = 1e5 + 5, M = 2e5 + 5;
struct Edge {
    int x, y, s, t;
}a[M];
struct node {
    int x, y;
    node(){}
    node(int x_, int y_){x = x_; y = y_;}
}sta[M];
int fa[N], dis[N], ans[N], sta1[M], siz[N];
int n, m, cnt, T, top, top1, flag;
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
vector<int> tr[N<<2];
inline void update(int k, int s, int t, int l, int r, int c) {
    if (s == l && r == t) {tr[k].push_back(c); return ;}
    int mid = (l + r) / 2;
    if (t <= mid) update(ls, s, t, l, mid, c);
    else if (s > mid) update(rs, s, t, mid + 1, r, c);
    else {
        update(ls, s, mid, l, mid, c);
        update(rs, mid + 1, t, mid + 1, r, c);
    }
}
inline int getf(int x) {return fa[x] == x ? x : getf(fa[x]);}
inline int getdis(int x) {
    int res = 0;
    while (x != fa[x]) {
        res ^= dis[x];
        x = fa[x];
    }
    return res;
}
inline void push(int k) {
    int all = tr[k].size();
    for (int i = 0; i < all; ++i) {
        int x = a[tr[k][i]].x, y = a[tr[k][i]].y;
        int f1 = getf(x), f2 = getf(y);
        if (f1 == f2) {if (!(getdis(x) ^ getdis(y))){flag = 1, sta1[++top1] = top; return ;}}//括起来
        else {
            if (siz[f1] > siz[f2]) swap(x, y), swap(f1, f2);
            siz[f2] += siz[f1]; dis[f1] = dis[x] ^ dis[y] ^ 1; fa[f1] = f2;
            sta[++top] = node(f1, f2);
        }
    }
    sta1[++top1] = top;
}
inline void pull() {
    if (top1 >= 0) top1--;
    while (top != sta1[top1]) {
        int x = sta[top].x, y = sta[top].y;
        dis[x] = 0; siz[y] -= siz[x]; fa[x] = x;
        top--;
    }
    flag = 0;
}
inline void solve(int k, int s, int t) {
    push(k);
    if (flag) {pull(); return ;}
    if (s == t) {
        ans[s] = 1;
        pull();
        return ;
    }
    int mid = (s + t) / 2;
    solve(ls, s, mid); solve(rs, mid + 1, t);
    pull();
}
int main() {
    n = read(); m = read(); T = read();
    for (int i = 1; i <= n; ++i) fa[i] = i, siz[i] = 1;
    for (int i = 1; i <= m; ++i) {
        int x = read(), y = read(), s = read(), t = read();
        if (s == t) continue; //要写,不然RE
        s++;
        a[i].x = x; a[i].y = y; a[i].s = s; a[i].t = t;
        update(1, s, t, 1, T, i);
    }
    solve(1, 1, T);
    for (int i = 1; i <= T; ++i) puts(ans[i] ? "Yes" : "No");
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值