[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;
}