题意
一张无向图,求有多少条路径使得路径上的花费小于L,这里路径上的花费是这样规定的,a、b两点之间的多条路径中的最长的边最小值。
题解
询问次数很多,考虑离线操作。
离线操作也是套路,因为路径花费是其中最大的,所以考虑从小到大加边,当前加边的边的边权一定大于等于已经加过的最大边的边权。利用这个性质,可以进行离线操作,每次加边即为将边所连接的两个点加入到并查集中。加入的边对答案的贡献为两个点所在并查集的中点个数的乘积。
依次离线处理所有询问.
代码
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 1e6+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
int tot, num, nowans;
int n, m, Q;
int fa[nmax], sz[nmax];
struct edge {
int from, to, w;
}e[nmax];
bool ecmp(edge a, edge b) {
return a.w < b.w;
}
struct query {
int id, l, ans;
}q[nmax];
bool qcmp(query a, query b) {
return a.l < b.l;
}
bool idcmp(query a, query b) {
return a.id < b.id;
}
void makeset() {
for(int i = 0; i <= n; ++i) {
fa[i] = i;
sz[i] = 1;
}
}
int findset(int x) {
while(x != fa[x]) x = fa[x];
return x;
}
bool unionset(int x, int y) {
x = findset(x);
y = findset(y);
if(x == y) {
return false;
} else {
nowans = sz[x] * sz[y];
if(sz[x] >= sz[y]) {
fa[y] = x;
sz[x] += sz[y];
} else {
fa[x] = y;
sz[y] += sz[x];
}
return true;
}
}
int main() {
while(scanf("%d %d %d", &n, &m, &Q) != EOF) {
makeset();
tot = num = 0;
for(int i = 1; i <= m; ++i) {
scanf("%d %d %d",&e[i].from, &e[i].to, &e[i].w);
}
for(int i = 1; i <= Q; ++i) {
scanf("%d", &q[i].l);
q[i].id = i;
q[i].ans = 0;
}
sort(e+1, e+m+1, ecmp);
sort(q+1, q+Q+1, qcmp);
int cur = 1;
for(int i = 1; i <= Q; ++i) {
q[i].ans = q[i - 1].ans;
while(e[cur].w <= q[i].l && cur <= m) {
if(unionset(e[cur].from, e[cur].to)) {
q[i].ans += nowans;
}
cur ++;
}
}
sort(q+1, q+Q+1, idcmp);
for(int i = 1; i <= Q; ++i) {
printf("%d\n", q[i].ans);
}
}
return 0;
}