hdu 5441 travel 离线+带权并查集

时间:2022-08-19 00:31:14

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
 
 
 
 
题意:给出一个图,有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就可以了
 
 
 
 
 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<set> using namespace std; struct Edge
{
int u,v,w;
};
Edge edge[+]; struct Query
{
int x,id;
};
Query qry[]; int w[+];
long long print[];
int fa[+]; void init(int n)
{
for(int i=;i<=n;i++){
fa[i]=i;
w[i]=;
}
} bool cmp1(Edge z,Edge y)
{
return z.w<y.w;
}
bool cmp2(Query z,Query y)
{
return z.x<y.x;
} int find_fa(int a)
{
if(fa[a]==a)
return a;
else{
fa[a]=find_fa(fa[a]);
w[fa[a]]+=w[a];
w[a]=;
return fa[a];
}
} void solve(int ,int ,int ); int main()
{
int test;
scanf("%d",&test);
while(test--){
int n,m,Q;
scanf("%d %d %d",&n,&m,&Q);
init(n);
for(int i=;i<=m;i++){
scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w);
}
for(int i=;i<=Q;i++){
scanf("%d",&qry[i].x);
qry[i].id=i;
}
solve(n,m,Q);
}
return ;
} void solve(int n,int m,int Q)
{
sort(edge+,edge+m+,cmp1);
sort(qry+,qry+Q+,cmp2); int cnt=;
long long sum=;
for(int i=;i<=Q;i++){
while(cnt<=m&&edge[cnt].w<=qry[i].x){
int fau=find_fa(edge[cnt].u);
int fav=find_fa(edge[cnt].v);
if(fau!=fav){
sum+=w[fau]*w[fav]*;
fa[fau]=fav;
w[fav]+=w[fau];
w[fau]=;
}
cnt++;
}
print[qry[i].id]=sum;
} for(int i=;i<=Q;i++){
printf("%I64d\n",print[i]);
} return ;
}