HihoCoder - 1629 Graph (莫队+并查集)

版权声明:本文为蒟蒻原创文章,转载请注明出处哦~ 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;
}

猜你喜欢

转载自blog.csdn.net/a54665sdgf/article/details/82501086
今日推荐