Codeforces 1566G Four Vertices

给定一张 \(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)\) 得到另一组合法的解,与假设矛盾。

根据引理,在这张新图上,我们分两种情况讨论:

  1. 答案包括了最小的边:那么我们只需要从小到大依次枚举另一条边,根据抽屉原理,只需要枚举至多 \(5\) 次就可以找到一条满足题意的边,所以这部分时间复杂度是 \(O(1)\) 的。
  2. 答案不包括最小的边:那么我们只需要在第一种情况找到的区间中暴力枚举一个 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\n", 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\n", 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;
}
posted @ 2021-09-16 12:06  syksykCCC  阅读(84)  评论(0编辑  收藏  举报