bzoj3237[Ahoi2013]连通图 cdq分治+并查集

3237: [Ahoi2013]连通图

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1431  Solved: 521
[Submit][Status][Discuss]

Description

Input

Output

Sample Input

4 5
1 2
2 3
3 4
4 1
2 4
3
1 5
2 2 3
2 1 2

Sample Output

第一反应LCT

然后我就失去知觉了

后来知道是cdq,mobier。

并查集+cdq分治

删边换成加边咯。

1.把一直存在的边加进来。

2.非左边询问的边都加进来

3.递归解决左区间

4.还原

5.把非右边的询问边都加进来

6.递归解决右区间

7.还原

好暴力啊。

并查集你可选择按秩合并,或者路径压缩。

你可选择可持久化,然而我直接暴力修改了。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N = 880000;
const int SZ = 5500000;
using namespace std;
int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3)  - '0' + ch; ch = getchar();}
    return x * f;
}
struct ask{int c, e[4];}q[N]; 
int f[N], st[SZ], u[N], v[N], del[N], ans[N], top, tim, n, m, k;
 
int find(int x) {
    if(f[x] == x) return x;
    st[++top] = x; st[++top] = f[x];
    f[x] = find(f[x]);
}
 
void Union(int u, int v) {
    int fu = find(u), fv = find(v);
    if(fu != fv) {
        st[++top] = fu; st[++top] = f[fu];
        f[fu] = fv;
    }
}
 
void Del(int L, int R) {
    for(int i = L; i <= R; ++i) 
        for(int j = 0;j < q[i].c; ++j) 
            del[q[i].e[j]] = tim;
}
void Add(int L, int R) {
    for(int i = L;i <= R; ++i) 
        for(int j = 0;j < q[i].c; ++j)
            if(del[q[i].e[j]] != tim) Union(u[q[i].e[j]], v[q[i].e[j]]);
}
void restore(int cur) {for( ;cur != top; top -= 2) f[st[top - 1]] = st[top];}
 
void cdq(int L, int R) {
    int now = top;
    if(L == R) {
        bool flag = true;
        for(int i = 0;i < q[L].c && flag; ++i) 
            if(find(u[q[L].e[i]]) != find(v[q[L].e[i]])) flag = false;
        restore(now);
        ans[L] = flag;
        return;
    }
    int mid = L + R >> 1; ++tim;
    Del(L, mid); Add(mid + 1, R);
    cdq(L, mid); restore(now); ++tim;
    Del(mid + 1, R); Add(L, mid);
    cdq(mid + 1, R); restore(now);
}
 
int main() {
    n = read(); m = read(); top = 0;
    for(int i = 1;i <= n; ++i) f[i] = i;
    for(int i = 1;i <= m; ++i) {u[i] = read(); v[i] = read();}
    k = read(); tim = 1;
    for(int i = 1;i <= k; ++i) {
        q[i].c = read();
        for(int j = 0;j < q[i].c; ++j) del[q[i].e[j] = read()] = tim;
    }
    for(int i = 1;i <= m; ++i) 
    if(del[i] != tim)
        Union(u[i], v[i]);
    cdq(1, k);
    for(int i = 1;i <= k; ++i)
        puts(ans[i] ? "Connected" : "Disconnected");
    return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值