hdu 5441 travel 离线+带权并查集

Time Limit: 1500/1000 MS (Java/Others)

 Memory Limit: 131072/131072 K (Java/Others)

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,T≤5, which represents the number of test case.

For each test case, the first line consists of three integers n,m and q where n≤20000,m≤100000,q≤5000. The Undirected Kingdom has n cities and mbidirectional 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 d≤100000. 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

1

5 5 3

2 3 6334

1 5 15724

3 5 5705

4 3 12382

1 3 21726

6000

10000

13000

Sample Output

2

6

12

Source

2015 ACM/ICPC Asia Regional Changchun Online

题意:给出一个图,有n个节点,m条边,q个询问

每一个询问给出x

问,图中有多少点对,点对有一条路径的边长中最大的一条边的权值<=x

注意:(a,b)和(b,a)算是不同的点对

在线的话,想不出什么好的解法

离线的话,先把询问的x和图中边的权值小到大排序

然后遍历一次询问

用并查集

w[i]表示若i是这个集合的代表节点,则w[i]=这个集合的点数,否则w[i]=0

当询问x时,我们要知道目前有多少个集合,集合的代表节点是谁,每一个集合的点数

则此时的点对数=各个集合的点数a*(a-1)的和

要快速找出每一个集合的点数,有数组w

那要怎么快速有多少个集合和集合的代表节点呢?

我刚开始的想法是用一个set保存每一个集合的代表节点,若一个节点不是代表节点了,就删除该节点,更行w

这样每次询问的时候需要遍历一遍set,还是太慢了

后来一想,不用啊,只要再有一个变量sum,每次合并集合的时候就更新sum就可以啦

sum+=w[fau]*w[fav]*2

这样对于每一个询问x,直接记录print=sum就可以了

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<set>
  5
  6 using namespace std;
  7
  8 struct Edge
  9 {
 10     int u,v,w;
 11 };
 12 Edge edge[100000+5];
 13
 14 struct Query
 15 {
 16     int x,id;
 17 };
 18 Query qry[5005];
 19
 20 int w[20000+5];
 21 long long print[5005];
 22 int fa[20000+5];
 23
 24 void init(int n)
 25 {
 26     for(int i=1;i<=n;i++){
 27         fa[i]=i;
 28         w[i]=1;
 29     }
 30 }
 31
 32 bool cmp1(Edge z,Edge y)
 33 {
 34     return z.w<y.w;
 35 }
 36 bool cmp2(Query z,Query y)
 37 {
 38     return z.x<y.x;
 39 }
 40
 41 int find_fa(int a)
 42 {
 43     if(fa[a]==a)
 44         return a;
 45     else{
 46         fa[a]=find_fa(fa[a]);
 47         w[fa[a]]+=w[a];
 48         w[a]=0;
 49         return fa[a];
 50     }
 51 }
 52
 53 void solve(int ,int ,int );
 54
 55 int main()
 56 {
 57     int test;
 58     scanf("%d",&test);
 59     while(test--){
 60         int n,m,Q;
 61         scanf("%d %d %d",&n,&m,&Q);
 62         init(n);
 63         for(int i=1;i<=m;i++){
 64             scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w);
 65         }
 66         for(int i=1;i<=Q;i++){
 67             scanf("%d",&qry[i].x);
 68             qry[i].id=i;
 69         }
 70         solve(n,m,Q);
 71     }
 72     return 0;
 73 }
 74
 75 void solve(int n,int m,int Q)
 76 {
 77     sort(edge+1,edge+m+1,cmp1);
 78     sort(qry+1,qry+Q+1,cmp2);
 79
 80     int cnt=1;
 81     long long sum=0;
 82     for(int i=1;i<=Q;i++){
 83         while(cnt<=m&&edge[cnt].w<=qry[i].x){
 84             int fau=find_fa(edge[cnt].u);
 85             int fav=find_fa(edge[cnt].v);
 86             if(fau!=fav){
 87                 sum+=w[fau]*w[fav]*2;
 88                 fa[fau]=fav;
 89                 w[fav]+=w[fau];
 90                 w[fau]=0;
 91             }
 92             cnt++;
 93         }
 94         print[qry[i].id]=sum;
 95     }
 96
 97     for(int i=1;i<=Q;i++){
 98         printf("%I64d\n",print[i]);
 99     }
100
101     return ;
102 }
时间: 2024-10-29 10:45:56

hdu 5441 travel 离线+带权并查集的相关文章

hdu 5441 Travel 离线带权并查集

Travel Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5441 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

HDU 3047 Zjnu Stadium 带权并查集

题目来源:HDU 3047 Zjnu Stadium 题意:给你一些人 然后每次输入a b c 表示b在距离a的右边c处 求有多少个矛盾的情况 思路:用sum[a] 代表a点距离根的距离 每次合并时如果根一样 判断sum数组是否符合情况 根不一样 合并两棵树 这里就是带权并查集的精髓 sum[y] = sum[a]-sum[b]+x 这里y的没有合并前b的根 #include <cstdio> #include <cstring> using namespace std; cons

HDU 3172 Virtual Friends 带权并查集 -秩

ll T; while(~scanf("%d",&T)){ while(T--) { = = ... 思路: 用秩合并,看了题解才发现 if(fx == fy)要输出当前集合的秩而不是0... #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> #include <vector> #include <map&

hdu 2818 Building Block(带权并查集)

题目: 链接:点击打开链接 题意: 有N个积木,1到N编号.进行一些操作P次,M:X Y把X积木放到Y的上面,如果X和Y相等请忽略.C:X 计算X积木下的积木数目. 思路: 带权并查集的题目,定义数组sum[i]表示i积木下面积木的数目.遇到操作M,就把X和Y合并到同一个集合中.我们视每个结点为1个 Pile,其中rank[i]就表示每个Pile处的积木的个数,Initially, there are N piles, and each pile contains one block.所以,ra

HDU 3172 Virtual Friends(带权并查集)

题目地址:HDU 3172 带权并查集水题.每次合并的时候维护一下权值.注意坑爹的输入.. 代码如下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #include <ctype.h> #include <queue> #inclu

HDU 3635 Dragon Balls(带权并查集)

题目地址:HDU 3635 加权并查集水题. 用num数组维护该城市有多少龙珠,用times数组维护每个龙珠运输了多少次.num数组在合并的时候维护.times数组由于每个都不一样,所以要在找根的时候递归来全部维护. 最终,x龙珠所在的城市就是x节点所在的根,x结点所在的跟的num数组值是该城市的龙珠数.times[x]为该龙珠运输了多少次. 代码如下: #include <iostream> #include <cstdio> #include <string> #i

hdu 2818 Building Block (带权并查集,很优美的题目)

Problem Description John are playing with blocks. There are N blocks (1 <= N <= 30000) numbered 1...N.Initially, there are N piles, and each pile contains one block. Then John do some operations P times (1 <= P <= 1000000). There are two kinds

How Many Answers Are Wrong HDU - 3038 (经典带权并查集)

题目大意:有一个区间,长度为n,然后跟着m个子区间,每个字区间的格式为x,y,z表示[x,y]的和为z.如果当前区间和与前面的区间和发生冲突,当前区间和会被判错,问:有多少个区间和会被判错. 题解:x,y,z表示从x开始到y的所有数字的和,那么x-1就表示从(x-1,y]的区间和.我们可以对区间的左边x-1寻找他的左端点,同时对区间右边y也寻找他的左端点,如果这两个左端点相等(设为l)那么他就是将区间了[l,y]拆分成了[l,x-1]和[x,y],我们判断一下区间和是不是相等的就可以了也就是w[

HDU - 3038 / 3048 (带权并查集) (待补)

题目链接:点我点我 题意: 题解: 两题代码差不多,放个3047的. 1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 using namespace std; 5 6 const int N=200010; 7 int Father[N],value[N]; 8 9 int find(int x){ 10 if(x==Father[x]) return x; 11 int tmp=Fa