题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5354
题意: 给出n个点m条边的无向图, 要你求出哪些点满足在原图中删掉该点后是一个二分图, 输出一个01序列, 1表示这个点被删后是个二分图。 (1≤n,m≤105)
思路:考虑用黑白染色来判断二分图。 使用分治的思想, 用并查集维护每个点的颜色。对于一个区间[l, r], 所有两端点都不在该区间内的边, 无论删掉该区间的任一个点这些边都将留在图中, 用这些边进行黑白染色, 如果出现冲突则[l, r]中的点答案都是0, 不用再递归子区间。 否则, 保留当前并查集的样子, 继续递归左右子区间, 这些边也因为已经考虑过不用再考虑了, 直到l与r相等时这个点答案才是1。 记得每次递归回来要把并查集回溯到原本的状态。 为了方便操作, 并查集不要路径压缩, 为了防止超时还要按秩合并。
PS:注意将两个并查集合并时, 根据原本的染色情况, 有一个集合可能要打上反转标记。
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define ls (x << 1)
#define rs (x << 1 | 1)
#define mid ((l + r) >> 1)
const int N = (int)1e5 + 10;
const int M = (int)2e6 + 10;
using namespace std;
int n, m;
int fa[N], sz[N], bj[N], ans[N];
vector<pair<int, int> > edge[N << 2];
int* stk[M]; int top, val[M];
int modf(int *x, int y){
stk[++ top] = x; val[top] = *x;
return (*x) = y;
}
void recall(int aim){
while (top != aim) {(*stk[top]) = val[top]; top --;}
}
pair<int, int > find(int x){
int val = 0;
while (fa[x] != x) val ^= 1 ^ bj[x], x = fa[x];
return make_pair(x, val ^ bj[x]);
}
void unnion(int x, int y, int z){
if (sz[x] < sz[y]) swap(x, y);
modf(&fa[y], x);
modf(&sz[x], sz[x] + sz[y]);
modf(&bj[y], bj[y] ^ z);
}
bool in(int x, int l, int r){
return l <= x && x <= r;
}
void solve(int, int, int);
void work(int, int, int, vector<pair<int, int> > &);
void solve(int l, int r, int x){
if (l == r) {ans[l] = 1; return;}
vector<pair<int, int> > ().swap(edge[ls]);
vector<pair<int, int> > ().swap(edge[rs]);
vector<pair<int, int> > tmp[2];
for (int j = 0; j < edge[x].size(); j ++){
int u = edge[x][j].first, v = edge[x][j].second;
if (in(u, l, mid) || in(v, l, mid)) edge[ls].push_back(edge[x][j]);
else tmp[0].push_back(edge[x][j]);
if (in(u, mid + 1, r) || in(v, mid + 1, r)) edge[rs].push_back(edge[x][j]);
else tmp[1].push_back(edge[x][j]);
}
work(l, mid, ls, tmp[0]);
work(mid + 1, r, rs, tmp[1]);
}
void work(int l, int r, int x, vector<pair<int, int > > & tmp){
int _top = top; bool flag = 0;
for (int j = 0; j < tmp.size(); j ++){
int u = tmp[j].first, v = tmp[j].second;
pair<int, int > r1 = find(u), r2 = find(v);
if (r1.first == r2.first){
if (r1.second == r2.second) {flag = 1; break;}
}
else unnion(r1.first, r2.first, r1.second != r2.second);
}
if (flag){
for (int i = l; i <= r; i ++) ans[i] = 0;
}
else solve(l, r, x);
recall(_top);
}
int main(){
int T;
for (scanf("%d", &T); T --; ){
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i ++) fa[i] = i, sz[i] = 1, bj[i] = 0;
vector<pair<int, int> > ().swap(edge[1]);
for (int i = 1, u, v; i <= m; i ++){
scanf("%d %d", &u, &v);
edge[1].push_back(make_pair(u, v));
}
solve(1, n, 1);
for (int i = 1; i <= n; i ++) printf("%d", ans[i]);
puts("");
}
return 0;
}