[HDU 5354] Bipartite Graph

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5354

题意: 给出n个点m条边的无向图, 要你求出哪些点满足在原图中删掉该点后是一个二分图, 输出一个01序列, 1表示这个点被删后是个二分图。 (1n,m105)

思路:考虑用黑白染色来判断二分图。 使用分治的思想, 用并查集维护每个点的颜色。对于一个区间[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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值