HDU - 5441 离线带权并查集

Travel

Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)

Total Submission(s): 4433    Accepted Submission(s): 1466

Problem Description
Jack likes to travel around the world, but he doesn’t like to wait. Now, he is traveling in the Undirected Kingdom. There are n cities and m bidirectional roads connecting the cities. Jack hates waiting too long on the bus, but he can rest at every city. Jack can only stand staying on the bus for a limited time and will go berserk after that. Assuming you know the time it takes to go from one city to another and that the time Jack can stand staying on a bus is x minutes, how many pairs of city (a,b) are there that Jack can travel from city a to b without going berserk?
 

Input
The first line contains one integer T,T5, which represents the number of test case.

For each test case, the first line consists of three integers n,m and q where n20000,m100000,q5000. The Undirected Kingdom has n cities and m bidirectional roads, and there are q queries.

Each of the following m lines consists of three integers a,b and d where a,b{1,...,n} and d100000. It takes Jack d minutes to travel from city a to city b and vice versa.

Then q lines follow. Each of them is a query consisting of an integer x where x is the time limit before Jack goes berserk.

 

Output
You should print q lines for each test case. Each of them contains one integer as the number of pair of cities (a,b) which Jack may travel from a to b within the time limit x.

Note that (a,b) and (b,a) are counted as different pairs and a and b must be different cities.
 

Sample Input
 
  
15 5 32 3 63341 5 157243 5 57054 3 123821 3 2172660001000013000
 
Sample Output
 
  
2612

题目描述:输入一个t,测试样例的个数,第二行由三个数n,m,q组成,输入一个n,城市的个数,输入一个m,路线的条数,输入一个q,q个询问,接下来m行由a,b,d组成,其中a,b为城市的编号,d表示两个城市之间的距离,最后q行由一个数x组成,表示能忍受的最长距离

输出在有多少个城市对满足Jack不会发疯的条件.

注意:每到一个新城市 jack的怒气会回复到0  城市对(1,2)和城市对(2,1)表示两个不同的对.

离线带权并查集(表示完全不知道是什么东西,大佬说什么就是什么)

做法就是把城市按相距的距离从小到大排序一遍,同时询问也从小到大排序一遍(附上代码)

struct LX

{
    int st;
    int ed;
    int s;
    bool operator < (const LX lx1) const
    {
        return s<lx1.s;
    }
}lx[max_m];

struct Q

{
    int di;
    int id;
    bool operator < (const Q q1) const
    {
        return di<q1.di;
    }
}q[5005];

之后就用并查集把城市之间连起来,用同一个父节点表示

int findf(int x)

{
    return x==root[x]?x:root[x]=findf(root[x]);//不断更新
}

然后按询问从小到大遍历一遍所有联通的城市(就算城市不止由一棵树构成,或者说就算一棵树上的城市之间的距离大于jack的暴怒值,jack也可以在距离小于暴怒值的城市间来回,就是说你要找到所有城市距离小于暴怒值的个数)(终于说清了~~~~~~~~~~)

当你查询到不同的树时可到达城市对的个数增加了sum[1]*sum[2]个(其实是sum[1]*sum[2]*2个)但我们先记为增加了这么多

同时把这两棵数合并成一棵,然后神奇操作来了

inline void unio(int x,int y)
{
    x=findf(x);y=findf(y);
    if(sum[x]>sum[y])
    {
        sum[x]+=sum[y];
        root[y]=x;
    }
    else
    {
        sum[y]+=sum[x];
        root[x]=y;
    }
}

平时我们合并的话可能是随便把一个当成父节点让另一个变成他的子节点,在这里我们让更大的那个变成父节点,另一个变成子节点

这样的话父节点的儿子会越变越多,这样在后面合并树的时候会显得很方便直接

之后只需要把每一个询问遍历得到的答案按询问的顺序存在一个数组里面就好了

最后按询问的顺序西输出就好了

注意之前我们只是按城市编号从小到大遍历了一遍,所以对于每一个询问只给出了一半的坐标.所以输出的时候需要乘一个2.

在这个题目里面输出ans[a]*2是会超时的,需要用到位运算输出ans[a]<<1,不然会超时的(我就超时了几遍差点心态炸了)

附上ac代码:

#include <iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int max_m=100000+5;
const int max_n=20000+5;
struct LX
{
    int st;
    int ed;
    int s;
    bool operator < (const LX lx1) const
    {
        return s<lx1.s;
    }
}lx[max_m];
struct Q
{
    int di;
    int id;
    bool operator < (const Q q1) const
    {
        return di<q1.di;
    }
}q[5005];
int root[max_n];
int sum[max_n];
int ans[5005];
int n,m,qt;
int findf(int x)
{
    return x==root[x]?x:root[x]=findf(root[x]);
}
inline void unio(int x,int y)
{
    x=findf(x);y=findf(y);
    if(sum[x]>sum[y])
    {
        sum[x]+=sum[y];
        root[y]=x;
    }
    else
    {
        sum[y]+=sum[x];
        root[x]=y;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&qt);
        for(int m1=1;m1<=m;m1++)
        {
            scanf("%d%d%d",&lx[m1].st,&lx[m1].ed,&lx[m1].s);
        }
        sort(lx+1,lx+m+1);
        for(int q1=1;q1<=qt;q1++)
        {
            scanf("%d",&q[q1].di);
            q[q1].id=q1;
        }
        sort(q+1,q+1+qt);
        for(int n1=1;n1<=n;n1++)
        {
            root[n1]=n1;
            sum[n1]=1;
        }
        int j=0;
        int m1=1,q1=1;
        for(;q1<=qt;q1++)
        {
            for(;m1<=m&&lx[m1].s<=q[q1].di;m1++)
            {
                if(findf(lx[m1].st)==findf(lx[m1].ed))
                {
                    continue;
                }
                j=j+sum[findf(lx[m1].st)]*sum[findf(lx[m1].ed)];
                unio(lx[m1].st,lx[m1].ed);
            }
            ans[q[q1].id]=j;
        }
        for(int q1=1;q1<=qt;q1++)
        {
            printf("%d\n",ans[q1]<<1);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ecjtu_17_TY/article/details/81041732