hdu-5441-Travel-带权并查集

题意 

给你n个城市m个边,每条边有一权值,表示路费

如果给出一笔钱,钱大于路费则表示该条路可以通过

通过这条路 则代表 点 (a,b)  和(b,a)是合法的点,合法的对数为2

k次查询,每次查询给出一笔钱,求该笔钱能走过的所有点的合法对数


对输入的边按权值排序,对输入的钱数按权值排序

对第一笔钱,把权值比钱小的边 全都加到一个并查集,

设两端点为t1,t2

   int f1=findx(t1);
   int f2=findx(t2);
 bingcha[f1]=f2;
那么也就是说,把t1,t2所在的两个联通块合并了,新的根节点为f2,

所以两个联通块新增加的边数,为f1块的点乘上f2快的每一个点,并且由于x,y和y,x算两对点,所以总的为2*fa[f1]*fa[f2]//fa一直维护的是 该联通块的节点的个数

 并且累计新增的合法对数  ans+=2*fa[f1]*fa[f2]   //      f1,f2为两个端点在并查集中的根节点  

并且辅助数组fa[f2]+=fa[f1]    //必须是f1,f2,不能是t1,t2   此时就维护该新的联通块的节点个数,因为f1块已经被合并到f2块,就没必要维护了(再也不会访问到)


-------

对这笔钱做完操作后记录下ans,转入下一笔钱,由于已经排序了,下笔钱大于等于本笔钱,一定能覆盖 之前算过的点,

所以只需要利用之前的结果,在原来已经添加了的边的基础上直接继续添加边,在原ans上直接累加直到所有边遍历完

最终复杂度O(N)

最后按要求输出答案即可、


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std; 
#define  inf 0x7fffffff  
struct node
{
    int l,r,val;
 
    
};

node tm[100050];

int cmp(node a,node b)
{
    return a.val<b.val; 
}
node mm[5005];
int bingcha[20050];
int findx(int x)   //x所在集合的根 强哥的
{
    if(x==bingcha[x])return x;
    return bingcha[x]=findx(bingcha[x]);
}
  int ANS[5005];
int fa[20050];

int main()
{
    int t;
    scanf("%d",&t);
    int i,j;
    int n,m,q;
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&q);
        for(  i=1;i<=n;i++) fa[i]=1; //初始化
        for(  i=1;i<=n;i++) bingcha[i]=i; //初始化
 
        for (i=1;i<=m;i++)
        {
            scanf("%d%d%d",&tm[i].l,&tm[i].r,&tm[i].val);
        }
        sort(tm+1,tm+1+m,cmp);
        for (i=1;i<=q;i++)
        {
            scanf("%d",&mm[i].val);
            mm[i].l=i;
        }
        sort(mm+1,mm+1+q,cmp);
        int last=1;
        int ans=0;
        for (i=1;i<=q;i++)
        {
            int num=mm[i].l;
            for (j=last;tm[j].val<=mm[i].val&&j<=m;j++)
            {
                    int t1=tm[j].l;
                int t2=tm[j].r; 

                if (t1==t2 )  continue;
        
                int f1=findx(t1);
                int f2=findx(t2);
                if (f1==f2) continue;

            
                bingcha[f1]=f2;
            
                ans+=2*fa[f1]*fa[f2];
            //    int tmp=fa[f1];
                fa[f2]+=fa[f1]; 
            //    fa[f2]+=tmp;

                
            }
            last=j; 
            ANS[num]=ans; 
        }
        for (i=1;i<=q;i++)
        printf("%d\n",ANS[i]);
        
        
    }
    return 0;
}


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值