Codeforces 1108F MST Unification MST + LCA
Codeforces 1108F MST + LCA
F. MST Unification
Description:
You are given an undirected weighted connected graph with \(n\) vertices and \(m\) edges without loops and multiple edges.
The \(i\)-th edge is \(e_i = (u_i, v_i, w_i)\); the distance between vertices \(u_i\) and \(v_i\) along the edge \(e_i\) is \(w_i\) (\(1 \le w_i\)). The graph is connected, i. e. for any pair of vertices, there is at least one path between them consisting only of edges of the given graph.
A minimum spanning tree (MST) in case of positive weights is a subset of the edges of a connected weighted undirected graph that connects all the vertices together and has minimum total cost among all such subsets (total cost is the sum of costs of chosen edges).
You can modify the given graph. The only operation you can perform is the following: increase the weight of some edge by \(1\). You can increase the weight of each edge multiple (possibly, zero) times.
Suppose that the initial MST cost is \(k\). Your problem is to increase weights of some edges with minimum possible number of operations in such a way that the cost of MST in the obtained graph remains \(k\), but MST is unique (it means that there is only one way to choose MST in the obtained graph).
Your problem is to calculate the minimum number of operations required to do it.
Input:
The first line of the input contains two integers \(n\) and \(m\) (\(1 \le n \le 2 \cdot 10^5, n - 1 \le m \le 2 \cdot 10^5\)) — the number of vertices and the number of edges in the initial graph.
The next \(m\) lines contain three integers each. The \(i\)-th line contains the description of the \(i\)-th edge \(e_i\). It is denoted by three integers \(u_i, v_i\) and \(w_i\) (\(1 \le u_i, v_i \le n, u_i \ne v_i, 1 \le w \le 10^9\)), where \(u_i\) and \(v_i\) are vertices connected by the \(i\)-th edge and \(w_i\) is the weight of this edge.
It is guaranteed that the given graph doesn't contain loops and multiple edges (i.e. for each \(i\) from \(1\) to \(m\) \(u_i \ne v_i\) and for each unordered pair of vertices \((u, v)\) there is at most one edge connecting this pair of vertices). It is also guaranteed that the given graph is connected.
Output
Print one integer — the minimum number of operations to unify MST of the initial graph without changing the cost of MST.
Sample Input:
8 10
1 2 1
2 3 2
2 4 5
1 4 2
6 3 3
6 1 3
3 5 2
3 7 1
4 8 1
6 2 4
Sample Output:
1
Sample Input:
4 3
2 1 3
4 3 4
2 4 1
Sample Output:
0
Sample Input:
3 3
1 2 1
2 3 2
1 3 3
Sample Output:
0
Sample Input:
3 3
1 2 1
2 3 3
1 3 3
Sample Output:
1
Sample Input:
1 0
Sample Output:
0
Sample Input:
5 6
1 2 2
2 3 1
4 5 3
2 4 2
1 4 2
1 5 3
Sample Output:
2
题目链接
题解:
你有一个图,你可以增加某些边的边权使得这张图的最小生成树变成唯一的并保持最小生成树权值和不变,要求最小化边权增加量
我的做法:首先随便找一个最小生成树,然后考虑非树边\((x, y, val)\), 如果\(x到y\)的最小生成树上唯一路径的边权最大值等于\(val\),那么这条非树边的边权要增加\(1\), 因为这条边会导致非唯一的最小生成树,然后我就写了个树链剖分......总复杂度\(O(mlogm + m log^2n)\), 但是这道题没有修改,根本用不着树链剖分,只要倍增LCA的时候顺便用一个东西记录路径最大值就好了,我凭空多了一个\(log\),跑了\(400ms\)
题解的做法:考虑跑\(kruskal\)的过程,一次考虑所有边权相同的边,去除那些连接了两个已联通点的边,剩下的边一个个加入,加入失败的边的数量贡献到答案里,复杂度\(O(mlogm)\),而且很短,很快,只要\(100ms\)
我的做法AC代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int head[N], pnt[N << 1], nxt[N << 1], val[N << 1], cnt;
map<int, int> vis[N];
int dep[N], dfn[N], top[N], fa[N], son[N], rnk[N], size[N], clk;
int mx[N << 2];
struct edge {
int x, y, v;
bool operator<(const edge &rhs) const {
return v < rhs.v;
}
void adjust() {
if(dep[x] > dep[y])
swap(x, y);
}
}seg[N];
int dsu[N];
int n, m, ans;
void add(int x, int y, int v) {
pnt[cnt] = y;
val[cnt] = v;
nxt[cnt] = head[x];
head[x] = cnt++;
}
int find(int x) {
return dsu[x] == x ? x : dsu[x] = find(dsu[x]);
}
void unite(int x, int y) {
int fx = find(x), fy = find(y);
if(fx != fy)
dsu[fx] = fy;
}
int kruskal() {
int res = 0;
sort(seg + 1, seg + m + 1);
for(int i = 1; i <= n; ++i)
dsu[i] = i;
for(int i = 1; i <= m; ++i) {
if(find(seg[i].x) == find(seg[i].y)) continue;
vis[seg[i].x][seg[i].y] = 1;
vis[seg[i].y][seg[i].x] = 1;
unite(seg[i].x, seg[i].y);
res += seg[i].v;
}
return res;
}
void dfs1(int rt, int pre, int depth) {
dep[rt] = depth;
son[rt] = -1;
size[rt] = 1;
fa[rt] = pre;
for(int i = head[rt]; ~i; i = nxt[i]) {
int j = pnt[i];
if(j == pre) continue;
if(!vis[rt].count(j)) continue;
dfs1(j, rt, depth + 1);
size[rt] += size[j];
if(son[rt] == -1 || size[j] > size[son[rt]])
son[rt] = j;
}
}
void dfs2(int rt, int t) {
top[rt] = t;
dfn[rt] = clk;
rnk[clk] = rt;
clk++;
if(son[rt] == -1)
return;
dfs2(son[rt], t);
for(int i = head[rt]; ~i; i = nxt[i]) {
int j = pnt[i];
if(j == fa[rt] || j == son[rt]) continue;
if(!vis[rt].count(j)) continue;
dfs2(j, j);
}
}
void pushup(int rt) {
mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]);
}
void build(int rt, int l, int r) {
mx[rt] = 0;
if(l == r)
return;
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
}
void update(int rt, int l, int r, int pos, int val) {
if(l == r) {
mx[rt] = val;
return;
}
int mid = l + r >> 1;
if(pos <= mid)
update(rt << 1, l, mid, pos, val);
else
update(rt << 1 | 1, mid + 1, r, pos, val);
pushup(rt);
}
int query(int rt, int l, int r, int L, int R) {
if(L <= l && r <= R)
return mx[rt];
int mid = l + r >> 1, ans = 0;
if(L <= mid)
ans = max(ans, query(rt << 1, l, mid, L, R));
if(mid < R)
ans = max(ans, query(rt << 1 | 1, mid + 1, r, L, R));
return ans;
}
int ask(int a, int b) {
int ans = 0, ta = top[a], tb = top[b];
while(ta != tb) {
if(dep[ta] < dep[tb])
swap(ta, tb), swap(a, b);
ans = max(ans, query(1, 1, n, dfn[ta], dfn[a]));
a = fa[ta];
ta = top[a];
}
if(a == b) return ans;
if(dep[a] > dep[b]) swap(a, b);
return max(ans, query(1, 1, n, dfn[son[a]], dfn[b]));
}
void input() {
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i) {
scanf("%d%d%d", &seg[i].x, &seg[i].y, &seg[i].v);
add(seg[i].x, seg[i].y, seg[i].v);
add(seg[i].y, seg[i].x, seg[i].v);
}
}
void init() {
clk = 1;
kruskal();
dfs1(1, 1, 0);
dfs2(1, 1);
build(1, 1, n);
for(int i = 1; i <= m; ++i) {
if(!vis[seg[i].x].count(seg[i].y)) continue;
seg[i].adjust();
update(1, 1, n, dfn[seg[i].y], seg[i].v);
}
}
int solve() {
for(int i = 1; i <= m; ++i) {
if(vis[seg[i].x].count(seg[i].y)) continue;
ans += seg[i].v == ask(seg[i].x, seg[i].y);
}
return ans;
}
int main() {
input();
init();
printf("%d\n", solve());
return 0;
}
题解做法AC代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
struct e {
int x, y, v;
bool operator<(const e &rhs) const {
return v < rhs.v;
}
}edges[N];
int dsu[N], n, m, ans, cnt;
int find(int x) {
return x == dsu[x] ? x : dsu[x] = find(dsu[x]);
}
bool unite(int x, int y) {
int fx = find(x), fy = find(y);
if(fx == fy)
return false;
dsu[fx] = fy;
return true;
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
dsu[i] = i;
for(int i = 1; i <= m; ++i)
scanf("%d%d%d", &edges[i].x, &edges[i].y, &edges[i].v);
sort(edges + 1, edges + m + 1);
int j = 1;
for(int i = 1; i <= m; ++i) {
j = i + 1;
while(j <= m && edges[j].v == edges[i].v)
++j;
cnt = j - i;
for(int k = i; k < j; ++k)
if(find(edges[k].x) == find(edges[k].y))
--cnt;
for(int k = i; k < j; ++k)
cnt -= unite(edges[k].x, edges[k].y);
ans += cnt;
i = j - 1;
}
printf("%d\n", ans);
return 0;
}
Codeforces 1108F MST Unification MST + LCA的更多相关文章
- Codeforces 1108F MST Unification(最小生成树性质)
题目链接:MST Unification 题意:给定一张连通的无向带权图.存在给边权加一的操作,求最少操作数,使得最小生成树唯一. 题解:最小生成树在算法导论中有这个性质: 把一个连通无向图的生成树边 ...
- CF1108F MST Unification
题目地址:CF1108F MST Unification 最小生成树kruskal算法的应用 只需要在算法上改一点点 当扫描到权值为 \(val\) 的边时,我们将所有权值为 \(val\) 的边分为 ...
- Codeforces 1108F (MST Unification) (树上倍增 or 改进 kruksal)
题意:给你一张n个节点和m条边的无向连通图, 你可以执行很多次操作,对某一条边的权值+1(对于每条边,可以不加,可以无限次加),问至少进行多少次操作,可以使这张图的最小生成树变得唯一,并且最小生成树的 ...
- MST Unification CodeForces - 1108F
#include<iostream> #include<cstring> #include<algorithm> using namespace std; ; in ...
- Codeforces 196E Opening Portals MST (看题解)
Opening Portals 我们先考虑如果所有点都是特殊点, 那么就是对整个图求个MST. 想在如果不是所有点是特殊点的话, 我们能不能也 转换成求MST的问题呢? 相当于我们把特殊点扣出来, 然 ...
- Codeforces 160D Edges in MST tarjan找桥
Edges in MST 在用克鲁斯卡尔求MST的时候, 每个权值的边分为一类, 然后将每类的图建出来, 那些桥就是必须有的, 不是桥就不是必须有. #include<bits/stdc++.h ...
- CF F. MST Unification (最小生成树避圈法)
题意 给一个无向加权联通图,没有重边和环.在这个图中可能存在多个最小生成树(MST),你可以进行以下操作:选择某条边使其权值加一,使得MST权值不变且唯一.求最少的操作次数. 分系:首先我们先要知道为 ...
- (F. MST Unification)最小生成树
题目链接:http://codeforces.com/contest/1108/problem/F 题目大意:给你n个点和m条边,然后让你进行一些操作使得这个图的最小生成树唯一,每次的操作是给某一条边 ...
- 【春训团队赛第四场】补题 | MST上倍增 | LCA | DAG上最长路 | 思维 | 素数筛 | 找规律 | 计几 | 背包 | 并查集
春训团队赛第四场 ID A B C D E F G H I J K L M AC O O O O O O O O O 补题 ? ? O O 传送门 题目链接(CF Gym102021) 题解链接(pd ...
随机推荐
- css3: background-image使用多个背景图像
CSS3 允许元素使用多个背景图像. background-image: url(img/ic_ms.png),url(img/icon_dutyfree_invite.png); <!DOCT ...
- Libx264 编码错误 Input picture width(320) is greater than stride (0)
Ffmpeg libx264编码出现 Input picture width(320) is greater than stride (0),问题出在视频格式不正确. libx264 编码要求输入源的 ...
- angularJS contenteditable 指令双向绑定
项目遇到需求有点奇葩:双击div使其可编辑,失去焦点后进行数据绑定 通过自定义指令完成 好了上代码: .directive('contentEditable', function() { return ...
- 腾讯云ubuntu memcached 安装
ubuntu安装 sudo apt-get install memcached 停止memcached服务:sudo systemctl stop memcached 激活memcached服务:s ...
- MySQL-怎样使update操作sleep一段时间
)) a on mytest.id=a.id set mytest.name='xiaowang';
- Python之Numpy库常用函数大全(含注释)
前言:最近学习Python,才发现原来python里的各种库才是大头! 于是乎找了学习资料对Numpy库常用的函数进行总结,并带了注释.在这里分享给大家,对于库的学习,还是用到时候再查,没必要死记硬背 ...
- 下拉刷新swipetoloadlayout的使用方法,以及自己定义头部
<20160930---------–更新内容 回过头看自己曾经写的这个博客非常多的废话 和效果并不适合大家去使用这个好用的控件 如今整理删掉了自己写的效果, 写了个最简单的实例给一起学习的新手 ...
- Creating Tabbed Applications
新建一个空工程,如图 新建类 using System; using UIKit; namespace TabbedApplication { public class TabController : ...
- ABAP 动态where 使用
REPORT ztest001_xch. TABLES: makt. DATA where_tab() OCCURS WITH HEADER LINE. DATA : wa_itab LIKE mak ...
- 【WordSearch】Word Search
Given a 2D board and a word, find if the word exists in the grid. The word can be constructed from l ...