bzoj [Ahoi2013]连通图【cdq分治+并查集】

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

Connected
Disconnected
Connected

HINT

N<=100000 M<=200000 K<=100000

解题思路:

注意到原图是一个连通图,所以我们判断一个集合的答案时只用判断其包含的边的两端是否连通即可。
考虑分治。首先连上所有集合中都没有的边。
对于左边的集合,先连上这些集合中没有而右边集合中有的边,递归处理。
对于右边的集合,先连上这些集合中没有而左边集合中有的边,递归处理。
到底层时判断该集合答案即可。

连通性肯定要用并查集维护,但这题递归后要撤销连边,所以用可持久化并查集或栈式并查集都可以。

#include<bits/stdc++.h>
using namespace std; 

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=200005,M=5000005;
int n,m,Q,idx,top,fa[N],ans[N],stk1[M],stk2[M];
struct edge{int x,y,idx;}e[N];
struct node{int cnt,c[5];}q[N];

int find(int x)
{
    if(x==fa[x])return x;
    int y=find(fa[x]);
    if(y!=fa[x])stk1[++top]=x,stk2[top]=fa[x],fa[x]=y;
    return y;
}

void solve(int l,int r)
{
    int Top=top;
    if(l==r)
    {
        for(int i=1;i<=q[l].cnt;i++)
            if(find(e[q[l].c[i]].x)!=find(e[q[l].c[i]].y)){ans[l]=0;break;}
        return;
    }
    int mid=l+r>>1;++idx;
    for(int i=l;i<=mid;i++)
        for(int j=1;j<=q[i].cnt;j++)e[q[i].c[j]].idx=idx;
    for(int i=mid+1;i<=r;i++)
        for(int j=1;j<=q[i].cnt;j++)
            if(e[q[i].c[j]].idx!=idx)
            {
                int x=find(e[q[i].c[j]].x),y=find(e[q[i].c[j]].y);
                if(x!=y)stk1[++top]=x,stk2[top]=fa[x],fa[x]=y;
            }
    solve(l,mid);
    while(top!=Top)fa[stk1[top]]=stk2[top],--top;
    ++idx;
    for(int i=mid+1;i<=r;i++)
        for(int j=1;j<=q[i].cnt;j++)e[q[i].c[j]].idx=idx;
    for(int i=l;i<=mid;i++)
        for(int j=1;j<=q[i].cnt;j++)
            if(e[q[i].c[j]].idx!=idx)
            {
                int x=find(e[q[i].c[j]].x),y=find(e[q[i].c[j]].y);
                if(x!=y)stk1[++top]=x,stk2[top]=fa[x],fa[x]=y;
            }
    solve(mid+1,r);
    while(top!=Top)fa[stk1[top]]=stk2[top],--top;
}

int main()
{
    //freopen("lx.in","r",stdin);
    n=getint(),m=getint();
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)e[i].x=getint(),e[i].y=getint();
    Q=getint();++idx;
    for(int i=1;i<=Q;i++)
    {
        q[i].cnt=getint();ans[i]=1;
        for(int j=1;j<=q[i].cnt;j++)
            e[q[i].c[j]=getint()].idx=idx;
    }
    for(int i=1;i<=m;i++)
        if(e[i].idx!=idx)
            fa[find(e[i].x)]=find(e[i].y);
    solve(1,Q);
    for(int i=1;i<=Q;i++)ans[i]?puts("Connected"):puts("Disconnected");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值