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;
}