给定一张 (n) 个点的图,初始包含 (m) 条给定的边。
支持两种操作,共 (q) 次:
-
0 u v
删除在 (u, v) 之间的边。 -
1 u v w
在 (u, v) 之间添加一条长度为 (w) 的边。
保证任意时刻图中没有重边和自环。
在所有操作前和每次操作后,求出在图中取出两条端点集合不相交的路径的长度之和的最小值。一条路径不能是单独的一个点。保证任意时刻图中边数 (ge 4)。
(4 le n, m le 10^5),(0 le q le 10^5),(1 le w le 10^9)。
2s, 256MB
首先可以发现,在图中任取三条边,最优解都一定不超过它们的距离之和。这个结论是显然的,因为任意三条边至少包含了 (4) 个端点,那么就必然可以取出一组不相交的路径。
那么我们就已经得知了取三条边的最优方案,接下来只需要考虑取两条边的最优方案。
引理:对于一个点 (u),如果它的度数 (>3),那么我们就把与它相连的较大的那些边删去,使得每个点的度数 (le 3),这个新图的答案与原图的答案相同。
证明:不妨设答案包含了 ((a,b)) 和 ((c, d)) 两条边,而 ((a,b)) 不是与 (a) 相连的边中前 (3) 小的,即至少有三条边 ((a, x), (a, y), (a, z))((x, y, z) 互不相同)都小于 ((a, b)) 的长度,那么根据抽屉原理,(x, y, z) 中至少有一个点不等于 (c) 且不等于 (d),那么我们就可以用这条边替换掉 ((a, b)) 得到另一组合法的解,与假设矛盾。
根据引理,在这张新图上,我们分两种情况讨论:
- 答案包括了最小的边:那么我们只需要从小到大依次枚举另一条边,根据抽屉原理,只需要枚举至多 (5) 次就可以找到一条满足题意的边,所以这部分时间复杂度是 (O(1)) 的。
- 答案不包括最小的边:那么我们只需要在第一种情况找到的区间中暴力枚举一个 pair 进行配对即可。因为第一种情况中找到的满足条件的边至多是第 (5) 小的边,那第二种情况中的两条边都不应当超过第 (4) 小,也是常数级别的,所以这部分时间复杂度也是 (O(1)) 的。
于是使用 set
等数据结构暴力维护边即可,时间复杂度 (O((m+ q) log n))。
#include <bits/stdc++.h>
typedef long long ll;
const int N = 1e5 + 5, M = N << 1;
int n, m, q, U[M], V[M], W[M];
std::set<std::pair<int, int>> val[N], vals;
std::map<std::pair<int, int>, int> edges;
int limit(int u) {
if(val[u].size() < 3) return INT_MAX;
return std::next(val[u].begin(), 2)->first;
}
void pop(int u) {
auto it = val[u].begin();
for(unsigned i = 0; i < 3 && i < val[u].size(); i++, it++) {
if(vals.count(*it)) vals.erase(*it);
}
}
void push(int u) {
auto it = val[u].begin();
for(unsigned i = 0; i < 3 && i < val[u].size(); i++, it++) {
if(it->first <= std::min(limit(U[it->second]), limit(V[it->second])))
vals.insert(*it);
}
}
void solve() {
ll res = (ll)vals.begin()->first + std::next(vals.begin(), 1)->first + std::next(vals.begin(), 2)->first;
int maxe = vals.begin()->second;
for(auto it = std::next(vals.begin()); it != vals.end(); it++) {
int id = it->second;
if(U[id] != U[maxe] && U[id] != V[maxe] && V[id] != U[maxe] && V[id] != V[maxe]) {
res = std::min(res, (ll)W[id] + W[maxe]);
for(auto a = std::next(vals.begin()); a != it; a++) {
for(auto b = std::next(a); b != it; b++) {
int x = a->second, y = b->second;
if(U[x] != U[y] && U[x] != V[y] && V[x] != U[y] && V[x] != V[y]) {
res = std::min(res, (ll)W[x] + W[y]);
}
}
}
printf("%lld
", res); return;
}
}
for(auto a = std::next(vals.begin()); a != vals.end(); a++) {
for(auto b = std::next(a); b != vals.end(); b++) {
int x = a->second, y = b->second;
if(U[x] != U[y] && U[x] != V[y] && V[x] != U[y] && V[x] != V[y]) {
res = std::min(res, (ll)W[x] + W[y]);
}
}
}
printf("%lld
", res);
}
int main() {
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; i++) {
scanf("%d %d %d", &U[i], &V[i], &W[i]);
if(U[i] > V[i]) std::swap(U[i], V[i]);
val[U[i]].insert({W[i], i}); val[V[i]].insert({W[i], i});
edges[{U[i], V[i]}] = i;
}
for(int i = 1; i <= n; i++) push(i);
solve(); scanf("%d", &q);
while(q--) {
int opt; scanf("%d", &opt);
if(!opt) {
int u, v; scanf("%d %d", &u, &v);
if(u > v) std::swap(u, v);
pop(u); pop(v);
int id = edges[{u, v}];
val[u].erase({W[id], id}); val[v].erase({W[id], id});
edges[{u, v}] = 0;
push(u); push(v);
} else {
m++; scanf("%d %d %d", &U[m], &V[m], &W[m]);
if(U[m] > V[m]) std::swap(U[m], V[m]);
pop(U[m]); pop(V[m]);
val[U[m]].insert({W[m], m}); val[V[m]].insert({W[m], m});
edges[{U[m], V[m]}] = m;
push(U[m]); push(V[m]);
}
solve();
}
return 0;
}