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