BZOJ 4025|二分图|CDQ分治|并查集|LCT

13 篇文章 0 订阅
9 篇文章 0 订阅

一条边有存在的时间 [start,end] ,问每个时刻该时图是不是二分图。

是二分图就不存在奇环,考虑并查集维护最大生成树,是否是奇环也很好判断了,两点间距离就是dis[a]+dis[b]-2*dis[LCA],由于我们只关心奇偶性,因此把加减法换成xor也是可以的,然后就变成了dis[a]^dis[b]了,加上非树边就是环了。

那么我们就考虑扫一遍时间,然后到了对应时间就加入边、删除边,维护连通性,但是并查集不资瓷解绑啊?

分治大法好!按时间分治,分治到对应的边,搞一下,然后我们只需要回退并查集到之前的版本就可以了;然后剩下的边就交给下一层处理。

另外考虑不用并查集的情况,我们可以LCT维护最大生成树,还是维护两点间xor值,如果不连通还是一样的添加边或删除边,如果形成了环,而且是奇环,加到集合中,删除的时候,如果是非树边,从集合中删除。那么集合空就是二分图。

代码%%%Po神
好像分治会比一些写的好的LCT慢。毕竟复杂度摆在这。

#include <cstdio>
#include <vector>
#include <algorithm>
#define FOR(i,j,k) for(i=j;i<=k;++i)
using namespace std;
const int N = 100005;
int fa[N], rnk[N], c[N], ver, stk[N];
int find(int x) {
    for (; x != fa[x]; x = fa[x]);
    return x;
}
int dist(int x) {
    int ans = 0;
    for (; fa[x] && fa[x] != x; x = fa[x]) ans ^= c[x];
    return ans;
}
void merge(int x, int y, int z) {
    x = find(x); y = find(y);
    if (x == y) return;
    if (rnk[x] > rnk[y]) swap(x, y);
    if (rnk[x] == rnk[y]) ++rnk[y], stk[++ver] = -y;
    fa[x] = y; c[x] = z; stk[++ver] = x;
}
void restore(int now) {
    for (; ver > now; --ver) {
        if (stk[ver] < 0) --rnk[-stk[ver]];
        else fa[stk[ver]] = stk[ver], c[stk[ver]] = 0;
    }
}
struct Edge {
    int x, y, s, t;
    Edge(int i = 0, int j = 0, int k = 0, int l = 0): x(i), y(j), s(k), t(l) { }
};
void divide(int l, int r, vector<Edge> &e) {
    int i, mid = l + r >> 1, now = ver;
    vector<Edge> L, R;
    for (vector<Edge>::iterator it = e.begin();
        it != e.end(); ++it) {
        if (it->s == l && it->t == r) {
            int a = find(it->x), b = find(it->y), c = dist(it->x) ^ dist(it->y) ^ 1;
            if (a != b) merge(a, b, c);
            else if (c & 1) {
                FOR(i,l,r) puts("No");
                restore(now);
                return;
            }
        } else if (it->t <= mid) L.push_back(*it);
        else if (it->s > mid) R.push_back(*it);
        else {
            L.push_back(Edge(it->x, it->y, it->s, mid));
            R.push_back(Edge(it->x, it->y, mid + 1, it->t));
        }
    }
    if (l == r) puts("Yes");
    else divide(l, mid, L), divide(mid + 1, r, R);
    restore(now);
}

int main() {
    int i, n, m, t, a, b, c, d;
    vector<Edge> v;
    scanf("%d%d%d", &n, &m, &t);
    FOR(i,1,n) fa[i] = i;
    FOR(i,1,m) {
        scanf("%d%d%d%d", &a, &b, &c, &d);
        ++c; if (c > d) continue;
        v.push_back(Edge(a, b, c, d));
    }
    divide(1, t, v);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值