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

题目链接

题意:给你一个含有n个点和m条边的无向图以及q组询问,每个询问包括两个值L,R,让你输出从L到R中能够相互连通的两点的个数。

解法:由于总度数为2m,所以我们可以把度数和为sqrt(2m)的点放在一个分块内,并把每个分块的右端点值保存下来。用两个变量L和R来枚举区间的左右端点。按照莫队的思想,先将询问按照左端点的分块以及右端点的大小进行排序。然后处理每个询问时,如果本次询问的左端点与上次询问的左端点在同一分块内,则直接右移R,将新加入的点与从该点到分块右边界的所有点加入并查集,同时计算出新增的答案贡献值,对左端点则撤销上次的操作,从分块右边界开始往左移,将这些点与右端点之间的点也加入并查集。

如果本次询问的左端点与上次询问的左端点在不同一分块内,则清除之前的所有操作,重新开始枚举。

注意如果询问的左右端点在同一分块内,则需要特判一下。

此外,更新左端点时则要用启发式合并,因为要撤销之前的操作。更新右端点的时候可以用路径压缩(话说用启发式合并似乎更快??)

AC代码:

#define FRER() freopen("i.txt","r",stdin)
#include
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 sta;
struct Query
{
    int l,r,i;
    bool operator<(const Query& b)const
    {
        return in[l]!=in[b.l]?lrBound[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

 

你可能感兴趣的:(莫队,并查集)