CF1108F 题解
题目传送门
题目大意:
给定一个有
修改操作不能改变 MST 的权值和。
思路:
修改操作不能改变 MST 的权值和。 这句话很关键,它告诉我们只能在最开始的最小生成树上考虑,否则情况就很冗杂了。
所以我们肯定得先求出原图的最小生成树。
不难想到当最小生成树不唯一时,会有其他非树边去替换最小生成树上的边,那么什么时候可以替换呢?
假设我们已经求出了原图的一颗最小生成树,并且扫描到一条非树边
设这条环上权值最大的树边为
为了防止这种情况发生,我们只需要将
求树上两点之间的最大边权可以用倍增 LCA 解决。
综上所述,我们先用
时间复杂度为
#include <queue>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 200010;
int n, m;
int h[N], e[N << 1], ne[N << 1], w[N << 1], idx;
struct node{
int a, b, w;
bool type;
bool operator< (const node &o) const {
return w < o.w;
}
}edges[N];
int p[N];
int dep[N], f[N][22], T;
int maxd[N][22]; //maxd[i][j] 表示节点 i 到它的 2^j 辈祖先这条路径上的最大边权
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int find(int x) {
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
void bfs(int s) {
queue<int> q;
q.push(s);
dep[s] = 1;
while(!q.empty()) {
int t = q.front();
q.pop();
for(int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if(dep[j]) continue;
dep[j] = dep[t] + 1;
f[j][0] = t;
maxd[j][0] = w[i];
for(int k = 1; k <= T; k++) {
f[j][k] = f[f[j][k - 1]][k - 1];
maxd[j][k] = max(maxd[j][k - 1], maxd[f[j][k - 1]][k - 1]);
}
q.push(j);
}
}
}
int lca(int x, int y) {
int res = -0x3f3f3f3f;
if(dep[x] > dep[y]) swap(x, y);
for(int i = T; i >= 0; i--)
if(dep[f[y][i]] >= dep[x]) {
res = max(res, maxd[y][i]);
y = f[y][i];
}
if(x == y) return res;
for(int i = T; i >= 0; i--)
if(f[x][i] != f[y][i]) {
res = max(res, maxd[x][i]);
res = max(res, maxd[y][i]);
x = f[x][i], y = f[y][i];
}
res = max(res, maxd[x][0]);
res = max(res, maxd[y][0]);
return res;
}
int main() {
memset(h, -1, sizeof h);
scanf("%d%d", &n, &m);
int a, b, w;
for(int i = 1; i <= m; i++) {
scanf("%d%d%d", &a, &b, &w);
edges[i] = {a, b, w};
}
sort(edges + 1, edges + m + 1);
int sum = 0;
for(int i = 1; i <= n; i++) p[i] = i;
int ori = 0; //随便找一个起点
for(int i = 1; i <= m; i++) {
a = edges[i].a, b = edges[i].b, w = edges[i].w;
int x = find(a), y = find(b);
if(x != y) {
p[x] = y;
add(a, b, w), add(b, a, w);
edges[i].type = true; //树边标记一下
++sum;
ori = a;
}
if(sum == n - 1) break;
}
T = (int)log2(n);
bfs(ori);
int res = 0;
for(int i = 1; i <= m; i++)
if(!edges[i].type) { //如果是非树边
int tmp = lca(edges[i].a, edges[i].b);
if(edges[i].w == tmp) ++res; //满足上述性质则累加答案
}
printf("%d", res);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】