HDU - 5441 Travel 并查集计数 + 联通块合并

1.题意:给你n个点 , m条边以及边上的值,给出q个查询,每个查询给出一个值,问对于每个查询有多少个(a,b)的点之间符合a - > b间有一条路小于查询值。(注: (a,b)和(b,a)看做不同点)

2.样例:

Input:

1
5 5 3
2 3 6334
1 5 15724
3 5 5705
4 3 12382
1 3 21726
6000
10000
13000

output:

2
6
12

10000时有: (2,3),(3,2),(3,5),(5,3),(2,5),(5,2);

以此类推

3.分析:

(1)对于给出的一个查询,我们需要找到所有小于等于它的边,所有的边组成的图(可能是不连通图),统计出点就能得到答案。

(2)如果一个图有3个点,一个图有2个点,那么把这两个图联通以后会在两个图点对的基础上多出 (3 * 2 )*2个(a,b)对,这里我们*2操作可以最后一起算 。

(3)有了以上以后,我们可以把加边的过程看作是合并联通快的过程,每次合并加上num[fx] *num[fy] 个点对来统计对数。

(4)用并查集来区分不同集合的点同时统计这个集合的点数目,因为若查询a > 查询 b 那么查询b中的点对在查询a中都是合适的,只需要找在查询b以上的边即可,所以我们可以排序查询由小到大,累加点对,优化时间。

4.代码:

#include <iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn = 100000 + 100;
struct Node//记录边的信息
{
    int from,to,val;
    bool operator <(const Node &another)const{//按照值由小到大排
         return val<another.val;
    }
};
struct Person//记录查询
{
    int id,heart;
    LL ans;//ans记录点对
};
bool cmp1(Person &per1,Person &per2){//按照输入顺序排序,用于输出结果
    return per1.id<per2.id;
}
bool cmp2(Person &per1,Person &per2){//按照查询值排序,用于优化时间
    return per1.heart<per2.heart;
}
int n,m,q;
Node node[maxn];
Person per[maxn];
int pre[maxn],num[maxn];
int finded(int x){
    int v = x;
    while(v!=pre[v])v = pre[v];
    int j = x;
    while(j!=v){
        int temp = pre[j];
        pre[j] = v;
        j = temp;
    }
    return v;
}
void join(int a,int b){
    pre[a] = b;
    num[b]+=num[a];//b成为根节点,加上a圈子的点数,记录b圈子点数!!
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
       scanf("%d%d%d",&n,&m,&q);
       for(int i = 0;i<=n;i++){
           pre[i] = i;
           num[i] = 1;//每个圈子初始时=是自己,点数为1
       }
       for(int i = 0;i<m;i++){
          scanf("%d%d%d",&node[i].from,&node[i].to,&node[i].val);
       }
       sort(node,node+m);
       for(int i = 0;i<q;i++){
           scanf("%d",&per[i].heart);
           per[i].id = i;
           per[i].ans = 0;
       }
       sort(per,per+q,cmp2);//先按照查询值排序
       int j = 0;
       LL sum = 0;//记录点对数目
       for(int i = 0;i<q;i++){//循环查询(由小到大)
           for(;j<m&&node[j].val<=per[i].heart;j++){//统计边(j不回溯,因为查询由小到大)
                int fx = finded(node[j].from);
                int fy = finded(node[j].to);
                if(fx!=fy){//合并联通快
                   sum+=(LL)(num[fx]*num[fy]);//合并
                   join(fx,fy);
                }
           }
           per[i].ans = sum;//统计
       }
       sort(per,per+q,cmp1);//按照id排序
       for(int i = 0;i<q;i++){//输出答案*2
           printf("%lld\n",per[i].ans*2);
       }
    }
    return 0;
}

5.疑问:

不知道为什么下面的代码就WA了,我只是用while从每条边入手判断的QAQ,有大佬看出错误可以留言,菜鸡感激不尽!

#include <iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn = 100000 + 100;
struct Node
{
    int from,to,val;
    bool operator <(const Node &another)const{
         return val<another.val;
    }
};
struct Person
{
    int id,heart;
    LL ans;
};
bool cmp1(Person &per1,Person &per2){
    return per1.id<per2.id;
}
bool cmp2(Person &per1,Person &per2){
    return per1.heart<per2.heart;
}
int n,m,q;
Node node[maxn];
Person per[maxn];
int pre[maxn],num[maxn];
int finded(int x){
    int v = x;
    while(v!=pre[v])v = pre[v];
    int j = x;
    while(j!=v){
        int temp = pre[j];
        pre[j] = v;
        j = temp;
    }
    return v;
}
void join(int a,int b){
    pre[a] = b;
    num[b]+=num[a];
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
       scanf("%d%d%d",&n,&m,&q);
       for(int i = 0;i<=n;i++){
           pre[i] = i;
           num[i] = 1;
       }
       for(int i = 0;i<m;i++){
          scanf("%d%d%d",&node[i].from,&node[i].to,&node[i].val);
       }
       sort(node,node+m);
       for(int i = 0;i<q;i++){
           scanf("%d",&per[i].heart);
           per[i].id = i;
           per[i].ans = 0;
       }
       sort(per,per+q,cmp2);
       int j = 0;
       int i = 0;
       while(i<m&&j<q){//初始这样写的
            if(node[i].val<=per[j].heart){
                int fx = finded(node[i].from);
                int fy = finded(node[i].to);
                if(fx!=fy){
                   per[j].ans+=(LL)(num[fx]*num[fy]);
                   join(fx,fy);
                }
                i++;
            }
            else{
                j++;
                if(j<q) per[j].ans += per[j-1].ans;
                else break;
            }
       }
       sort(per,per+q,cmp1);
       for(int i = 0;i<q;i++){
           printf("%lld\n",per[i].ans*2);
       }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40772692/article/details/81949206