[JOISC 2014] 電圧 题解

[JOISC 2014] 電圧 题解

赛时都想到了我也不知道为啥自己没敢写

首先题意可以转化为,我们去掉一个边后,剩下的图可以黑白染色,同时保证去掉的边两端的点颜色相同,问这样的边数。换句话说,去掉一条边后,剩下的图应该是一个二分图。

然后我们很容易想到线段树分治来处理这种问题。每次只有一条边被删掉,那我们可以令第 \(i\) 条边的存在范围变成 \([1, i-1]\)\([i+1, m]\),这样在时刻 \(i\),就是在验证删掉第 \(i\) 条边是否合法了。剩下的就是线段树分治板子了。

当然,我们还得验证去掉的边两端可以染成一种颜色。我们用扩展域并查集来做,只需判断一下 \(u\)\(v + n\) 是否在一个集合中就行了。如果在一个集合中,则说明 \(u, v\) 必须异色;否则要么 \(u, v\) 不在一个连通块内,要么就是在一个联通块内但是同色。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100, M = 2e5+10;

int read() {
    int x = 0; char ch = getchar();
    while(ch<'0' || ch>'9') ch = getchar();
    while(ch>='0'&&ch<='9') x = x*10+(ch-48), ch = getchar();
    return x;
}
//断开每一条边判断是否为二分图 -> 一条边存在于 1 - i-1, i+1 - n
int n, m;
struct Edge{
    int u, v;
}e[M];

struct xwx{
    int son, fa, val;
}stk[M<<1];
int top;
struct DSU{
    int fa[N<<1], siz[N<<1]; 
    void init() {
        for(int i = 1; i<=n*2; ++i) {
            fa[i] = i, siz[i] = 1;
        }
    }
    int find(int x) {
        while(x != fa[x]) x = fa[x];
        return x;
    }
    void merge(int x, int y) {
        x = find(x), y = find(y);
        if(x == y) {
            return;
        }
        if(siz[x] > siz[y]) swap(x, y);
        fa[x] = y;
        siz[y]+=siz[x];
        stk[++top] = (xwx) {x, y, siz[x]};
        return;
    }
}dsu;

int ans;
struct SegmentTree{
    vector<int> tree[M<<2];
    #define ls tr<<1
    #define rs tr<<1 | 1
    void modify(int tr, int L, int R, int lq, int rq, int id) {
        if(lq == L && R == rq) {
            tree[tr].push_back(id);
            return;
        }
        int mid = (L+R)>>1;
        if(lq <= mid) modify(ls, L, mid, lq, min(rq, mid), id);
        if(mid < rq) modify(rs, mid+1, R, max(mid+1, lq), rq, id);
    }
    void solve(int tr, int L, int R, bool flag) {
        int u, v;
        
        int st = top;
        for(int id : tree[tr]) {
            u = e[id].u, v = e[id].v;
            dsu.merge(u, v + n);
			dsu.merge(v, u + n);
			if(dsu.find(u) == dsu.find(v)) flag = 0;
        }
        if(L == R) {
            if(flag) {
                u = e[L].u, v = e[L].v;
                if(dsu.find(u) != dsu.find(v+n)) ++ans;
            }
            while(top > st) {
               xwx tmp = stk[top];
                dsu.fa[tmp.son] = tmp.son;
                dsu.siz[tmp.fa] -= tmp.val;
                --top;
            }
            return;
        }
        int mid = (L+R)>>1;
        solve(ls, L, mid, flag);
        solve(rs, mid+1, R, flag);
        while(top > st) {
            xwx tmp = stk[top];
            dsu.fa[tmp.son] = tmp.son;
            dsu.siz[tmp.fa] -= tmp.val;
            --top;
        }
    }
}seg;
int main() {
    n = read(), m = read();
    for(int i = 1; i<=m; ++i) {
        e[i] = (Edge){read(), read()};
    }
    dsu.init();
    for(int i = 1; i<=m; ++i) {
        if(i > 1) seg.modify(1, 1, m, 1, i-1, i);
        if(i < m) seg.modify(1, 1, m, i+1, m, i);
    }
    seg.solve(1, 1, m, 1);
    printf("%d\n", ans);
    return 0;
}
posted @ 2023-09-24 15:47  霜木_Atomic  阅读(14)  评论(0编辑  收藏  举报