HDU 5441 Travel(并查集 离线查询)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41661919/article/details/86621712

题意:

       给出一张带边权的无向图,q次查询,每次查询给出一权值,问保留图中小于等于该权值的边同时去除其他边后所产生的能相互到达的点对数(x,y)和(y,x)属于不同情况。

思路:

       首先我们知道对于一个联通块来说,其中上述点对数就等于该联通块中节点的数目C(n,2)==n*(n-1)/2。所以可以考虑统计当前限制条件下各个联通块中点的数目,到这里我们会想到dfs划块和并查集两种操作。但又考虑到如果对于每次查询均重新建图,5*5000*1e5已经显然超时,所以我们需要只建一次图,并寻找一种能动态(一边建图一边查各块中点的数目)查询各联通块中节点数目的方案,所以肯定就是并查集了,为每个联通块的代表元添加一个表示当前节点数目的秩,合并联通块时很容易更新这个秩大小,同时为了只建一次图就算出所有答案,我们可以离线处理所有查询并对其排序,同样也对所有边按边权排序。这样复杂度可以近似为5*(5000+1e5)。

AC代码:

/** Wjunjie **/
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include<algorithm>
#include <set>
#include <queue>
#include <stack>
#include<vector>
#include<map>
#include<ctime>
#define LL long long
using namespace std;
const int N=2e4+100;
const int M=100000+100;
const int mod=1e8+7;
struct query
{
    int index,v;
    LL res;
}que[N];
bool cmp1(query aa,query bb)
{
    return aa.v<bb.v;
}
bool cmp2(query aa,query bb)
{
    return aa.index<bb.index;
}
struct edge
{
    int l,r,v;
    bool operator <(const edge & obj)const
    {
        return v<obj.v;
    }
}e[M];

int fat[N];
int num[N];
int find(int x)
{
    if(x==fat[x])return x;
    return fat[x]=find(fat[x]);
}
void unionn(int fa,int fb)//启发式合并,可能数据小看不出来?
{
    if(num[fa]<num[fb])
    {
        fat[fa]=fb;
        num[fb]=num[fa]+num[fb];
    }
    else
    {
        fat[fb]=fa;
        num[fa]=num[fa]+num[fb];
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m,q;
        scanf("%d%d%d",&n,&m,&q);
        memset(e,0,sizeof(e));
        memset(que,0,sizeof(que));
        memset(num,0,sizeof(num));
        memset(fat,0,sizeof(fat));
        for(int i=0;i<=n;++i)fat[i]=i,num[i]=1;
        for(int i=1;i<=m;++i)scanf("%d%d%d",&e[i].l,&e[i].r,&e[i].v);

        for(int i=1;i<=q;++i)scanf("%d",&que[i].v),que[i].index=i;
        sort(e+1,e+1+m);
        sort(que+1,que+1+q,cmp1);
        int cnt=1;
        for(int i=1;i<=q;++i)
        {
            LL sum=0;
            while(e[cnt].v<=que[i].v&&cnt<=m)//别忘了防止溢出
            {
                int fa=find(e[cnt].l),fb=find(e[cnt].r);
                if(fa!=fb)unionn(fa,fb);
                cnt++;

            }
            for(int j=1;j<=n;++j)
                if(fat[j]==j)sum+=(LL)num[j]*(LL)(num[j]-1);//这个组合公式挺好用的
            que[i].res=sum;

        }
        sort(que+1,que+1+q,cmp2);
        for(int i=1;i<=q;++i)printf("%lld\n",que[i].res);

    }
    return 0;
}

The end;

猜你喜欢

转载自blog.csdn.net/qq_41661919/article/details/86621712