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 ...
随机推荐
- 【每日Scrum】第四天(4.25) TD学生助手Sprint2站立会议
站立会议 组员 昨天 今天 困难 签到 刘铸辉 (组长) 和小楠重写架构,使代码更加简洁,并增加了几个界面 今天增加了几个页面的子菜单,然后设计了几个要用的界面 遇到的困难是,菜单的事件处理真是神一样 ...
- Day1 [上]- 认识Python
python简单介绍: python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为AB ...
- MongoDB的一些操作技巧
去年三月底入职上海的一家互联网公司,由于项目使用的是MongoDB数据库所以有机会接触了MongoDB.在项目的开发过程中使用系统原有的一些方法查询MongoDB感觉很费力,用起来也不爽,所以私下里就 ...
- HDU5363:Key Set
Problem Description soda has a set S with n integers {1,2,-,n}. A set is called key set if the sum o ...
- mongodb 安装、启动
MongoDB 之 你得知道MongoDB是个什么鬼 MongoDB - 1 最近有太多的同学向我提起MongoDB,想要学习MongoDB,还不知道MongoDB到底是什么鬼,或者说,知道是数据 ...
- NOIP考前感悟
闭关这么久,后来突然后悔自己前几天和暑假的状态很頽 不然进步也还能多一点吧 还好提前发现了,最后也还是努力了一把 也算不枉费自己的选择吧 从初中开始学习OI,到头来也没有什么成果 但还好自己高一 也还 ...
- linux杂谈(十四):ftp的企业应用级的配置(一)
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/linux_player_c/article/details/24869877 1.ftp简单介绍 ( ...
- ThinkPHP RBAC权限管理机制
RBAC是ThinkPHP很好用的后台权限管理的,话不多说,实现方法如下,也方便以后自己查询使用: 1.新建4个数据库表 self_role权限表 CREATE TABLE `self_role` ( ...
- 【bzoj2286】[Sdoi2011]消耗战
虚树入门题: #include<cstdio> #include<cstring> #include<algorithm> #include<ctime> ...
- CSU - 1547 Rectangle —— DP(01背包)
题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1547 题解: 关键是怎么处理长度为1的长方形.当长度为1的长方形的个数cnt> ...