题意:
有n个点,给你m条无向边,然后有q次询问,每次询问给你一个L,问你对于u到v的所有路径中的每条路径中最长的边的最小值不超过L的这样的点对有多少。
思路:首先考虑每个点对,因为点对之间的决定值只是由最大边来决定。所以贪心一下,边按照从小到大连接,如果之前已经被连接了,那么后面没必要再连接了。
然后每次,询问跑一次是不现实的。嗯,因为大的情况一定包含比它小的情况,所以我们可以离线,排序,处理一次就可以。
判断连接自然就是并查集了。不过注意合并的时候,增加的点数是两棵树的点数的乘积。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1e4+5;
const int inf = 1e9;
int n,m,q;
struct edge
{
int u,v,w;
bool operator < (const edge &a)const
{
return w < a.w;
}
}edge[MAXN*5];
struct query
{
int id;
int key;
bool operator < (const query &a)const
{
return key < a.key;
}
}query[MAXN];
int ans[MAXN];
int pre[MAXN];
int findx(int x)
{
return pre[x] == x? x : pre[x] = findx(pre[x]);
}
int sum_point[MAXN];
int connect(int u,int v)
{
u = findx(u);
v = findx(v);
int ans = 0;
if(u != v)
{
ans = sum_point[u]*sum_point[v];
pre[v] = pre[u];
sum_point[u] += sum_point[v];
}
return ans;
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&q))
{
for(int i = 0; i < m; ++i)
{
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
}
for(int i = 0; i < q; ++i)
{
query[i].id = i;
scanf("%d",&query[i].key);
}
sort(edge,edge+m);
sort(query,query+q);
for(int i = 1; i <= n; ++i)
{
pre[i] = i;
sum_point[i] = 1;
}
int j = 0;
int sum = 0;
for(int i = 0; i < q; ++i)
{
while(j < m && edge[j].w <= query[i].key)
{
sum += connect(edge[j].u,edge[j].v);
j++;
}
ans[query[i].id] = sum;
}
for(int i = 0;i < q; ++i)printf("%d\n",ans[i]);
}
return 0;
}