关闭

[CodeChef]GERALD07/[JZOJ4739]Ztxz16学图论

标签: OI并查集莫队算法CodeChefLCT
694人阅读 评论(0) 收藏 举报
分类:

题目大意

一个n个点,m条边的无向图。有q对询问,每次询问如果只保留编号在[li,ri]的边,图中有多少个联通块。

1n,m,q2×105


题目分析

考虑使用莫队算法。求联通块个数的经典思路是并查集,但是并查集仅支持添加操作,所以我们要使用类似[JZOJ4663]Seq的方法,在处理同一个块的询问时,左指针位于块尾。使用一个并查集维护右指针右移的影响,每次处理询问的时候再使用另一个并查集维护左指针左移的影响(这个并查集“套”在前一个并查集上面),记录改变的位置,处理完询问之后暴力还原。思路是十分简单的,如果是第一次见这种姿势不太明白,可以去看看我上面说的那道题。
时间复杂度O((q+m)mα(n))
其实本题可以在线。以编号为键值,使用LCT维护最大生成树,接着使用可持久化线段树搞一搞可以做到O((q+m)log2n)。但是由于本蒟蒻不会这个做法,于是在这里也只是口胡一下。如果想了解可以戳这里看官方题解。
Update(2016/09/03)
在线做法是以编号为键值,然后使用我们按照编号从小到大加边,使用LCT来维护各个连通块包含1i的最大生成树。
查询区间[l,r]的时候,我们可以先查询包含[1,r]的所有边的连通块个数,由于我们曾经维护了[1,r]的最大生成树,这个是很容易维护的。那么我们只需要看一下这棵最大生成树里面有多少条编号在[1,l)的边(主席树维护),原本连通块个数加上这些边的条数便是答案。


代码实现

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

const int N=200050;
const int M=200050;
const int Q=200050;

int fa[N],Fa[N],cg[N];
int n,m,q,bs,T;
bool mark[N];
int ans[Q];

struct D
{
    int l,r,id;
}qy[Q];

struct Edge
{
    int x,y;

    Edge (int x0=0,int y0=0){x=x0,y=y0;}
}es[M];

bool operator<(D x,D y){return (x.l-1)/bs<(y.l-1)/bs||(x.l-1)/bs==(y.l-1)/bs&&x.r<y.r;}

int lcur,rcur,now;

int getfather(int son){return fa[son]==son?son:fa[son]=getfather(fa[son]);}
int GetFather(int son){return Fa[son]==son?son:Fa[son]=GetFather(Fa[son]);}

void add(int x,bool tp)
{
    if (lcur>rcur) return;
    int u=es[x].x,v=es[x].y,fx=getfather(u),fy=getfather(v);
    if (tp)
    {
        if (!mark[fx]) mark[fx]=1,cg[++cg[0]]=fx;
        if (!mark[fy]) mark[fy]=1,cg[++cg[0]]=fy;
        int Fx=GetFather(fx),Fy=GetFather(fy);
        if (Fx!=Fy) now--,Fa[Fy]=Fx;
    }
    else if (fx!=fy) now--,fa[fy]=fx;
}

void recover(){for (;cg[0];cg[0]--) mark[Fa[cg[cg[0]]]=cg[cg[0]]]=0;}

void solve()
{
    bs=trunc(sqrt(m))+1,sort(qy+1,qy+1+q);
    for (int i=1,tmp,tail;i<=q;i++)
    {
        if (i==1||(qy[i].l-1)/bs!=(qy[i-1].l-1)/bs)
        {
            for (int j=1;j<=n;j++) Fa[j]=fa[j]=j;
            now=n,lcur=tail=min(((qy[i].l-1)/bs+1)*bs,m),rcur=0;
        }
        while (rcur<qy[i].r) add(++rcur,0);
        tmp=now;
        while (lcur>qy[i].l) add(--lcur,1);
        ans[qy[i].id]=now,now=tmp,recover(),lcur=tail;
    }
}

int main()
{
    freopen("gerald07.in","r",stdin),freopen("gerald07.out","w",stdout);
    for (T=read();T;T--)
    {
        n=read(),m=read(),q=read();
        for (int i=1,u,v;i<=m;i++) u=read(),v=read(),es[i]=Edge(u,v);
        for (int i=1;i<=q;i++) qy[i].l=read(),qy[i].r=read(),qy[i].id=i;
        solve();
        for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
    }
    fclose(stdin),fclose(stdout);
    return 0;
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:106817次
    • 积分:3491
    • 等级:
    • 排名:第9969名
    • 原创:228篇
    • 转载:0篇
    • 译文:0篇
    • 评论:60条
    博客专栏
    OI

    文章:14篇

    阅读:14029
    最新评论
    文章分类