CF1483D-Useful Edges
题目大意
有一个 \(n\) 个结点的无向加权图,以及 \(q\) 个三元组,\((u,v,l)\) ,其中 \(u\) , \(v\) 是顶点,\(l\) 是正整数。
如果存在至少一个三元组和一个具有以下特性的路径(不一定简单),则边 \(e\) 被称为 有用。
· 这条路径的端点是 \(u\) 和 \(v\)。
· \(e\) 是这条路径的一条边。
· 这条路径上所有边的权重之和不超过 \(l\) 。
求这张图中有用边的数量。
题解
设城市 \(u\) 到 城市 \(v\) 的路径长度为 \(e[u][v]\) ,最短距离为 \(d[u][v]\) ,时间预算为 \(l[u][v]\) 。
根据题意,如果 \(i \to j\) 为 城市\(u\) 和城市 \(v\) 间的重要路径,则满足 \(d[u][i]+e[i][j]+d[j][v]\le l[u][v]\) 。
直接枚举 \(u,v,i,j\) 是 \(O(n^4)\) 的,显然不可以接受,于是考虑优化。一般来说,对于满足这样的不等关系的式子,我们可以通过减少未知量来降低复杂度。
在此,我们不妨移项,将上式转变为 \(e[i][j]+d[j][v]\le l[u][v]-d[u][i]\) 。这样可以只进行三方的枚举,就能分别求出不等号两边的量了。
我们可以先枚举 \(u,v,i\) 预处理出不等号的右边,注意是 \(\le\) ,所以要使不等号右边的值最大。然后再枚举 \(i,j,v\) ,判断是否符合条件即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ls(p) (p << 1)
#define rs(p) (ls(p) ^ 1)
typedef pair<int, int> pii;
const int INF = 1e16;
const int N = 605;bool vis[N][N];
int d[N][N], w[N][N], l[N][N];
pii e[N * N];
inline int read();
inline void solve()
{int n = read(), m = read();memset(d, 0x3f, sizeof(d));memset(w, 0x3f, sizeof(w));for (int i = 1; i <= n; ++i)d[i][i] = w[i][i] = 0;for (int i = 1; i <= m; ++i){auto &[u, v] = e[i];u = read(), v = read();w[u][v] = w[v][u] = d[u][v] = d[v][u] = read();}for (int k = 1; k <= n; ++k)for (int i = 1; i <= n; ++i)for (int j = 1; j <= n; ++j)d[i][j] = min(d[i][j], d[i][k] + d[k][j]);int q = read(), ans = 0;while (q--){int u = read(), v = read();l[u][v] = l[v][u] = read();}for (int u = 1; u <= n; ++u){for (int i = 1; i <= n; ++i){int mx = -1e18;for (int v = 1; v <= n; ++v){mx = max(mx, l[u][v] - d[i][v]);}for (int j = 1; j <= n; ++j){if (d[u][j] + w[j][i] <= mx)vis[i][j] = 1;}}}for (int i = 1; i <= m; ++i){auto [u, v] = e[i];if (vis[u][v] || vis[v][u])ans++;}printf("%lld\n", ans);
}signed main()
{int T = 1;while (T--)solve();return 0;
}inline int read()
{int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-')f = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}return x * f;
}