题解 P9701【[GDCPC2023] Classic Problem】
题如其名,确实挺经典的。
我们称边权在输入中给定的边为特殊边,其它边为平凡边。称特殊边涉及到的点为特殊点,其它点为平凡点。
显然,对于连续的若干平凡点 ,他们内部的最优连边方式就是连成一条链,花费 的代价。我们先把这样的代价加到答案中,然后将极长连续平凡点缩成一个点,称为区间点。
我们可以按照编号从小到大的顺序为区间点和特殊点重新标号。问题转化为给定一个由区间点和特殊点构成的 点的完全图,求它的最小生成树。
有特殊性质的点数特别多的稠密图的最小生成树问题一般考虑使用 Boruvka 算法进行求解。对于图 求最小生成树,Boruvka 算法的思路是:每一轮找到每个连通块向外连的边权最小的边,在这轮结束后把所有最小边连上,直到整个图连通为止。显然,每一轮过后连通块数至少减半,因此轮数为 ,复杂度为 。我们可以利用图的特殊性质,加速找到最小边的过程,使得复杂度降至 。
在每一轮开始时,预处理 表示节点 前面/后面最近的不在同一连通块的点。对于区间点,它的最小边一定是它与 或 之间的。对于特殊点,首先判一遍所有跨连通块的特殊边,然后分别找到前面/后面最近的既不在同一连通块又没有特殊边相连的点,看看哪个更小即可。可以证明,每轮的时间复杂度为 ,总复杂度为 。由于重建的图的点数 ,总复杂度为 。
// Problem: T368344 [GDCPC2023] L-Classic Problem
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/T368344?contestId=135929
// Memory Limit: 1 MB
// Time Limit: 8000 ms
//
// Powered by CP Editor (https://cpeditor.org)
//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x, y, z) for(ll x = (y); x <= (z); ++x)
#define per(x, y, z) for(ll x = (y); x >= (z); --x)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do {freopen(s".in", "r", stdin); freopen(s".out", "w", stdout);} while(false)
#define endl '\n'
using namespace std;
typedef long long ll;
mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
ll randint(ll L, ll R) {
uniform_int_distribution<ll> dist(L, R);
return dist(rnd);
}
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
const ll N = 5e5 + 5, inf = 0x1f1f1f1f1f1f1f1fll;
ll T, n, m, U[N], V[N], W[N], buc[N], tot, cntV, val[N], pos[N], pre[N], suc[N], vis[N];
map<ll, ll> id;
struct Vertex {
ll l, r;
bool special;
vector<tuple<ll, ll>> e;
}vtx[N];
struct Dsu {
ll fa[N];
void init(ll x) {rep(i, 1, x) fa[i] = i;}
bool isroot(ll x) {return x == fa[x];}
ll find(ll x) {return isroot(x) ? x : fa[x] = find(fa[x]);}
bool same(ll x, ll y) {return find(x) == find(y);}
bool merge(ll x, ll y) {
if(same(x, y)) return false;
x = find(x); y = find(y);
fa[x] = y;
return true;
}
}dsu;
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
for(cin >> T; T; --T) {
cin >> n >> m;
rep(i, 1, m) {
cin >> U[i] >> V[i] >> W[i];
buc[++tot] = U[i];
buc[++tot] = V[i];
}
sort(buc + 1, buc + 1 + tot);
tot = unique(buc + 1, buc + 1 + tot) - buc - 1;
ll lst = 0;
rep(i, 1, tot) {
// segment
if(buc[i] - lst + 1 >= 3) {
++cntV;
vtx[cntV].l = lst + 1;
vtx[cntV].r = buc[i] - 1;
vtx[cntV].special = false;
}
// special
++cntV;
vtx[cntV].l = vtx[cntV].r = buc[i];
vtx[cntV].special = true;
id[buc[i]] = cntV;
lst = buc[i];
}
// segment
if(n - lst + 1 >= 2) {
++cntV;
vtx[cntV].l = lst + 1;
vtx[cntV].r = n;
vtx[cntV].special = false;
}
rep(i, 1, m) {
vtx[id[U[i]]].e.emplace_back(id[V[i]], W[i]);
vtx[id[V[i]]].e.emplace_back(id[U[i]], W[i]);
}
ll edges = 0, ans = 0;
rep(u, 1, cntV) ans += vtx[u].r - vtx[u].l;
dsu.init(cntV);
while(edges < cntV - 1) { // MST - Boruvka algorithm
rep(u, 1, cntV) {
if(dsu.isroot(u)) {
val[u] = inf;
pos[u] = -1;
}
}
rep(u, 1, cntV) {
if(vtx[u].special) {
for(auto [v, w] : vtx[u].e) {
if(!dsu.same(u, v) && w < val[dsu.find(u)]) {
val[dsu.find(u)] = w;
pos[dsu.find(u)] = dsu.find(v);
}
}
}
}
pre[1] = 0;
rep(i, 2, cntV) pre[i] = dsu.same(i - 1, i) ? pre[i - 1] : i - 1;
suc[cntV] = cntV + 1;
per(i, cntV - 1, 1) suc[i] = dsu.same(i, i + 1) ? suc[i + 1] : i + 1;
rep(u, 1, cntV) {
if(vtx[u].special) for(auto [v, w] : vtx[u].e) vis[v] = 1;
for(ll v = u; v >= 1; ) {
if(dsu.same(v, u)) v = pre[v];
else if(vis[v]) --v;
else {
if(vtx[u].l - vtx[v].r < val[dsu.find(u)]) {
val[dsu.find(u)] = vtx[u].l - vtx[v].r;
pos[dsu.find(u)] = dsu.find(v);
}
break;
}
}
for(ll v = u; v <= cntV; ) {
if(dsu.same(u, v)) v = suc[v];
else if(vis[v]) ++v;
else {
if(vtx[v].l - vtx[u].r < val[dsu.find(u)]) {
val[dsu.find(u)] = vtx[v].l - vtx[u].r;
pos[dsu.find(u)] = dsu.find(v);
}
break;
}
}
if(vtx[u].special) for(auto [v, w] : vtx[u].e) vis[v] = 0;
}
rep(u, 1, cntV) {
if(dsu.isroot(u) && pos[u] != -1 && dsu.isroot(pos[u])) {
++edges;
ans += val[dsu.find(u)];
dsu.merge(u, pos[u]);
}
}
}
cout << ans << endl;
rep(i, 1, tot) buc[i] = id[i] = 0;
rep(i, 1, cntV) {
vis[i] = vtx[i].l = vtx[i].r = 0;
vtx[i].special = false;
vtx[i].e.clear();
}
tot = cntV = 0;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现