版权声明:本文为蒟蒻原创文章,转载请注明出处哦~ https://blog.csdn.net/a54665sdgf/article/details/82501086
题意:给你一个含有n个点和m条边的无向图以及q组询问,每个询问包括两个值L,R,让你输出从L到R中能够相互连通的两点的个数。
解法:由于总度数为2m,所以我们可以把度数和为sqrt(2m)的点放在一个分块内,并把每个分块的右端点值保存下来。用两个变量L和R来枚举区间的左右端点。按照莫队的思想,先将询问按照左端点的分块以及右端点的大小进行排序。然后处理每个询问时,如果本次询问的左端点与上次询问的左端点在同一分块内,则直接右移R,将新加入的点与从该点到分块右边界的所有点加入并查集,同时计算出新增的答案贡献值,对左端点则撤销上次的操作,从分块右边界开始往左移,将这些点与右端点之间的点也加入并查集。
如果本次询问的左端点与上次询问的左端点在不同一分块内,则清除之前的所有操作,重新开始枚举。
注意如果询问的左右端点在同一分块内,则需要特判一下。
此外,更新左端点时则要用启发式合并,因为要撤销之前的操作。更新右端点的时候可以用路径压缩(话说用启发式合并似乎更快??)
AC代码:
#define FRER() freopen("i.txt","r",stdin)
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
const int Q=1e5+10;
int head[N],degree[N],prv[N],nxt[N<<1],to[N<<1],in[N],rBound[N],nEdge,Size[N],fa[N],now,ans[Q];
int n,m,q,sz;
stack<int> sta;
struct Query
{
int l,r,i;
bool operator<(const Query& b)const
{
return in[l]!=in[b.l]?l<b.l:r<b.r;
}
} qr[Q];
void AddEdge(int u,int v)
{
nxt[nEdge]=head[u],to[nEdge]=v,head[u]=nEdge++;
nxt[nEdge]=head[v],to[nEdge]=u,head[v]=nEdge++;
degree[u]++,degree[v]++;
}
int Find(int x)
{
return ~fa[x]?Find(fa[x]):x;
}
void Union(int u,int v,int flag)
{
int x=Find(u),y=Find(v);
if(x==y)return;
if(Size[x]<Size[y])swap(x,y);
now+=Size[x]*Size[y];
Size[x]+=Size[y];
fa[y]=x;
if(!flag)sta.push(y);
}
void cancel()
{
while(!sta.empty())
{
int u=sta.top();
sta.pop();
Size[fa[u]]-=Size[u];
now-=Size[u]*Size[fa[u]];
fa[u]=-1;
}
}
void solve()
{
sort(qr,qr+q);
int block=-1,L,R;
for(int i=0; i<q; ++i)
{
int l=qr[i].l,r=qr[i].r;
if(in[l]!=block)
{
fill(fa+1,fa+n+1,-1);
fill(Size+1,Size+n+1,1);
block=in[l];
R=rBound[block];
now=0;
}
while(R<r)
{
for(int e=head[++R]; ~e; e=nxt[e])
{
int v=to[e];
if(v>rBound[block]&&v<=r)Union(R,v,1);
}
}
for(L=min(r,rBound[block]); L>=l; --L)
{
for(int e=head[L]; ~e; e=nxt[e])
{
int v=to[e];
if(v>=l&&v<=r)Union(L,v,0);
}
}
ans[qr[i].i]=now;
cancel();
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(head,-1,sizeof head);
memset(degree,0,sizeof degree);
nEdge=0;
scanf("%d%d%d",&n,&m,&q);
sz=sqrt(m<<1)+1.5;
for(int i=1; i<=m; ++i)
{
int u,v;
scanf("%d%d",&u,&v);
AddEdge(u,v);
}
for(int i=1,cntd=0; i<=n; ++i)
{
cntd+=degree[i];
in[i]=cntd/sz;
rBound[in[i]]=i;
}
for(int i=0; i<q; ++i)
{
scanf("%d%d",&qr[i].l,&qr[i].r);
qr[i].i=i;
}
solve();
for(int i=0; i<q; ++i)printf("%d\n",ans[i]);
}
return 0;
}