hdu 4677 并查集+离线+构造 无向图上连续区间的连通块数目

9 篇文章 0 订阅
3 篇文章 0 订阅

把n个点按sqrt(n)的大小划分,第i个区间表示【1,sqrt(n)*i】的连通关系,

离线处理,从后往前边插入边询问,插入u点时,如果[u,v]的v点在区间内,就放在同一集合

有u点的询问对【u,v】时,由于v之前的那个区间的连通关系已经处理好,只须处理v所在区间的起点到v这个区间的连通关系

这个区间不超过sqrt(n)个点,每个点有m/n条边(由于数据是随机产生的),每次复杂度是m/n*sqrt(n),刚好不超,构造奇妙

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <cstring>
using namespace std;
#define N 30050
struct Q
{
    int r,idx;
    Q(int _r=0,int _idx=0):r(_r),idx(_idx){}
};
vector<Q>que[N];
vector<int>edge[N];
int fa[N][200],scc[200],rr[200];
int ta[N],first[N],ans[N];
int n,m,tot,every;
int find(int i,int pos)
{
    return fa[i][pos]==i?i:fa[i][pos]=find(fa[i][pos],pos);
}
void init()
{
    for(int i=0;i<=n;++i)
        edge[i].clear(),que[i].clear();
    memset(scc,0,sizeof(scc));
    every=tot=sqrt(n*1.0);
    if(n*n!=tot) tot++;
    for(int i=0;;++i)
    {
        rr[i]=i*every;
        if(i*every>=n) break;
    }
    for(int i=0;i<=tot;++i)
        for(int j=0;j<=n;++j)
        fa[j][i]=j;
    memset(first,-1,sizeof(first));
}
int main ()
{
    int T;scanf("%d",&T);
    for(int kk=1;kk<=T;++kk)
    {
        scanf("%d%d",&n,&m);
        init();
        for(int i=1,u,v;i<=m;++i)
        {
            scanf("%d%d",&u,&v);
            if(u==v) continue;
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        int q;scanf("%d",&q);
        for(int i=1,u,v;i<=q;++i)
        {
            scanf("%d%d",&u,&v);
            que[u].push_back(Q(v,i));
        }
        int run_clock=0;
        for(int u=n;u>=1;--u)
        {
            for(int j=1;j<=tot;++j)
            {
                if(u>rr[j]) continue;
                scc[j]++;
                int inc=0;
                for(int i=0;i<edge[u].size();++i)
                {
                    int v=edge[u][i];
                    if(u<v&&v<=rr[j])
                    {
                        int u1=find(u,j);
                        int v1=find(v,j);
                        if(u1!=v1)
                        {
                            inc++;
                            fa[v1][j]=u1;
                        }
                    }
                }
                scc[j]-=inc;
            }
            for(int z=0;z<que[u].size();++z)
            {
                run_clock++;
                int right=que[u][z].r;
                int pos;int inc=0;
                for(pos=0;pos<tot;++pos)
                    if(rr[pos]<right&&right<=rr[pos+1]) break;
                for(int i=max(u+1,rr[pos]+1);i<=right;++i)
                {
                    if(first[i]!=run_clock)
                    {
                        first[i]=run_clock;
                        fa[i][tot+1]=i;
                    }
                    for(int j=0;j<edge[i].size();++j)
                        if(u<=edge[i][j]&&edge[i][j]<i)
                        {
                            int v1=find(edge[i][j],pos);
                            if(first[v1]!=run_clock)
                            {
                                first[v1]=run_clock;
                                fa[v1][tot+1]=v1;
                            }
                            if(find(i,tot+1)!=find(v1,tot+1))
                            {
                                fa[find(v1,tot+1)][tot+1]=find(i,tot+1);
                                inc++;
                            }
                        }
                }
                ans[que[u][z].idx]=scc[pos]+right-max(rr[pos],u-1)-inc;
            }
        }
        printf("Case #%d:\n",kk);
        for(int i=1;i<=q;++i)
            printf("%d\n",ans[i]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值