HDU 3938 Portal
找两点间最长边(一条路径)的最小值(所有路径),这个值的意义在本题中相当于这两点可以在某种意义上连通了,然后给定一个值L
问你最多能连通多少种两点。
需要注意的是L
不是和的上限,而是单个两点间费用的上限。所以只用想有多少种两点间的费用小于等于L
就行了。
而两点间的费用必然等于某条边的权值。
要知道,边的权值按从小到大离散分布,那么L
可以退化到某个小于等于它的边的权值上。
所以,每次union
返回的是本次union
带来全图新增的连通的两点对的个数,是增量。那么本次边的权值所对应的查询结果就是历史连通对个数加上本次增量。
由于边的权值范围太大,用unordered_map
替代数组。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <unordered_map>
using namespace std;
const int MAXN = 1e4 + 5;
int N, M, Q;
int pre[MAXN];
unordered_map<int, int> mk; // 这里用map时间差距不大
struct Edge
{
int n1, n2, cost;
bool operator<(const Edge& e) const
{
return cost < e.cost;
}
};
vector<Edge> ve;
void init()
{
ve.clear();
memset(pre, -1, sizeof pre);
mk.clear();
}
int f(int x)
{
int f0 = x, f1 = x;
for (; pre[f0] > 0;) f0 = pre[f0];
for (; pre[f1] > 0;)
{
int t = f1;
f1 = pre[f1];
pre[t] = f0;
}
return f0;
}
int u(int n1, int n2)
{
int f1 = f(n1);
int f2 = f(n2);
if (f1 != f2)
{
int t = -(pre[f1] + pre[f2]);
int r = (t*(t - 1)) / 2 - ((-pre[f1] * (-pre[f1] - 1)) / 2 + (-pre[f2] * (-pre[f2] - 1)) / 2);
if (pre[f1] <= pre[f2])
{
pre[f1] += pre[f2];
pre[f2] = f1;
}
else
{
pre[f2] += pre[f1];
pre[f1] = f2;
}
return r;
}
return 0;
}
int main()
{
int a, b, c;
for (; ~scanf("%d%d%d", &N, &M, &Q);)
{
init();
for (int i = 0; i < M; i++)
{
scanf("%d%d%d", &a, &b, &c);
ve.push_back({ a,b,c });
}
sort(ve.begin(), ve.end());
int total = 0;
for (int i = 0; i < ve.size(); i++)
{
total += u(ve[i].n1, ve[i].n2);
mk[ve[i].cost] = total;
}
for (int i = 0; i < Q; i++)
{
scanf("%d", &a);
int ans = 0;
for (int j = a; j >= 0; j--)
{
if (mk.find(j) != mk.end())
{
mk[a] = mk[j];
ans = mk[j];
break;
}
}
printf("%d\n", ans);
}
}
return 0;
}