BZOJ3237 [AHOI2013]连通图

题目大意

给出 n 个点, m 条边的无向连通图,同时给出 q 个询问,求删除给定的 c 条边之后,整张图是否连通。

数据范围

1n105,1m2×105,1c4

思路

我们将整个操作序列视为时间轴,考虑线段树分治,用链表预处理出每条边在图中出现的时间,然后线段树上分治,分治的时候,用按秩合并的并查集维护连通性,我们分治到叶子节点的时候,必然是当前询问的那些边没有建出来,由于图是连通的,所以直接询问删除的边是否连通即可。

考虑这样做的时候,我们分出来的区间的个数,每一个询问操作会增加四个线段树上的标记,所以总的区间数是 O(n) 级别的,还有并查集,所以总复杂度为 O(qlognlogq)

代码常数比较大,卡着时限过的

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>

using std :: vector;
using std :: map;

template <typename Tp>Tp Max(const Tp &a, const Tp &b) {return a > b ? a : b;}
template <typename Tp>Tp Min(const Tp &a, const Tp &b) {return a < b ? a : b;}

template <typename Tp>void Read(Tp &x) {
    Tp in = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') f = -1; ch = getchar();}
    while(ch>='0' && ch<='9') {in = in*10+ch-'0'; ch = getchar();}
    x = in*f;
}

const int SN = 100000 + 10;

const int SM = 200000 + 10;

int rnk[SN], fa[SN], sta[SM * 50], n, m, top, QUE;

struct Edge {
    int u, v, l, r;
};

struct EDGE {
    int u, v;
}Q[SM];

struct Que {
    int k;
    int c[5];
}q[SN];

vector <Edge> all;
vector <int> time[SM];

int find(int x) {
    while(fa[x] != x) x = fa[x];
    return fa[x];
}

void Unionn(int x, int y) {
    if(rnk[x] > rnk[y]) std :: swap(x, y);
    if(rnk[x] == rnk[y]) rnk[y]++, sta[++top] = -y;
    fa[x] = y, sta[++top] = x;
}

void reverse(int bottom) {
    int now;
    while(top > bottom) {
    now = sta[top--];
    if(now < 0) rnk[-now]--;
    else fa[now] = now;
    }
}

void CDQ(int l, int r, vector <Edge> &p) {

    int bottom = top;

    vector <Edge> L, R;

    vector <Edge> :: iterator it;

    int mid = (l + r) >> 1;

    int L1, R1, x, y, a, b;

    for(it = p.begin(); it != p.end(); it++) {

    x = (*it).u, y = (*it).v;
    L1 = (*it).l, R1 = (*it).r;
    a = find(x), b = find(y);

    if(L1 == l && R1 == r) {
        if(a != b) Unionn(a, b);
    }
    else {
        if(L1 <= mid) {
        Edge New = (*it);
        if(New.r <= mid) L.push_back(New);
        else New.r = mid, L.push_back(New);
        }
        if(R1 > mid) {
        Edge New = (*it);
        if(New.l > mid) R.push_back(New);
        else New.l = mid + 1, R.push_back(New);
        }
    }
    }

    if(l == r) {
    for(int i = 1; i <= q[l].k; i++) {
        a = q[l].c[i];
        x = Q[a].u, y = Q[a].v;
        a = find(x), b = find(y);
        if(a != b) {
        puts("Disconnected"); reverse(bottom); return ;
        }
    }
    puts("Connected"); reverse(bottom); return ;
    }

    CDQ(l, mid, L), CDQ(mid + 1, r, R);

    reverse(bottom);
}

int main() {    
    int x, y, a, b;

    Read(n), Read(m);

    for(int i = 1; i <= n; i++) fa[i] = i;

    for(int i = 1; i <= m; i++) {
    Read(Q[i].u), Read(Q[i].v);
    if(Q[i].u > Q[i].v) std :: swap(Q[i].u, Q[i].v);
    }

    Read(QUE);

    for(int i = 1; i <= QUE; i++) {
    Read(q[i].k);
    for(int j = 1; j <= q[i].k; j++) {
        Read(q[i].c[j]);
        x = q[i].c[j];
        time[x].push_back(i);
    }
    }

    vector <int> :: iterator it;

    for(int i = 1; i <= m; i++) 
    if(!time[i].size()) {
        x = Q[i].u, y = Q[i].v;
        a = find(x), b = find(y);
        if(a != b) Unionn(a, b);
    }
    else {
        int pre = 1;
        for(it = time[i].begin(); it != time[i].end(); it++) {
        if((*it) == pre) {pre = (*it) + 1; continue ;}
        all.push_back((Edge){Q[i].u, Q[i].v, pre, (*it) - 1});
        pre = (*it) + 1;
        }
        it--;
        if((*it) != QUE) all.push_back((Edge){Q[i].u, Q[i].v, pre, QUE});
    }

    CDQ(1, QUE, all);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值