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的更多相关文章

  1. Codeforces 1108F MST Unification(最小生成树性质)

    题目链接:MST Unification 题意:给定一张连通的无向带权图.存在给边权加一的操作,求最少操作数,使得最小生成树唯一. 题解:最小生成树在算法导论中有这个性质: 把一个连通无向图的生成树边 ...

  2. CF1108F MST Unification

    题目地址:CF1108F MST Unification 最小生成树kruskal算法的应用 只需要在算法上改一点点 当扫描到权值为 \(val\) 的边时,我们将所有权值为 \(val\) 的边分为 ...

  3. Codeforces 1108F (MST Unification) (树上倍增 or 改进 kruksal)

    题意:给你一张n个节点和m条边的无向连通图, 你可以执行很多次操作,对某一条边的权值+1(对于每条边,可以不加,可以无限次加),问至少进行多少次操作,可以使这张图的最小生成树变得唯一,并且最小生成树的 ...

  4. MST Unification CodeForces - 1108F

    #include<iostream> #include<cstring> #include<algorithm> using namespace std; ; in ...

  5. Codeforces 196E Opening Portals MST (看题解)

    Opening Portals 我们先考虑如果所有点都是特殊点, 那么就是对整个图求个MST. 想在如果不是所有点是特殊点的话, 我们能不能也 转换成求MST的问题呢? 相当于我们把特殊点扣出来, 然 ...

  6. Codeforces 160D Edges in MST tarjan找桥

    Edges in MST 在用克鲁斯卡尔求MST的时候, 每个权值的边分为一类, 然后将每类的图建出来, 那些桥就是必须有的, 不是桥就不是必须有. #include<bits/stdc++.h ...

  7. CF F. MST Unification (最小生成树避圈法)

    题意 给一个无向加权联通图,没有重边和环.在这个图中可能存在多个最小生成树(MST),你可以进行以下操作:选择某条边使其权值加一,使得MST权值不变且唯一.求最少的操作次数. 分系:首先我们先要知道为 ...

  8. (F. MST Unification)最小生成树

    题目链接:http://codeforces.com/contest/1108/problem/F 题目大意:给你n个点和m条边,然后让你进行一些操作使得这个图的最小生成树唯一,每次的操作是给某一条边 ...

  9. 【春训团队赛第四场】补题 | 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 ...

随机推荐

  1. WeX5开发指南

    WeX5入门.UI2开发.App开发.服务端开发.扩展资料学习. 1 新手入门 1.1 运行WeX5的demo(视频) 1.2 App开发.调试.打包部署完整过程(视频) 1.3 创建第一个应用(视频 ...

  2. ORACLE 查看表结构

    select table_name from user_tables; //当前用户的表 select table_name from all_tables; //所有用户的表 select tabl ...

  3. Java基础知识查漏 一

    Java基础知识查漏 一 Jdk和jre Jdk是java程序设计师的开发工具,只要包含编译程序,jvm和java函数库 Jre中只有jvm和java函数库,没有编译程序的相关工具,适合只运行不撰写j ...

  4. ZeroMQ Distributed Messaging

    ZeroMQ \zero-em-queue\, \ØMQ\: Ø  Connect your code in any language, on any platform. Ø  Carries mes ...

  5. SVN在myeclipse中新建地址的时出现被拒绝

    新建资源库地址: 控制台提示信息: svn: connection refused by the server svn: connection refused by the server Connec ...

  6. 阻止SSIS import excel时的默认行为

    为什么SSIS总是错误地获取Excel数据类型,以及如何解决它! 由Concentra发布 2013年5月15日 分享此页面 分享   发现Concentra的分析解决方案 Concentra的分析和 ...

  7. STM32 ~ 外扩SRAM

    字节控制功能.支持高/低字节控制. 看看实现 IS62WV51216 的访问,需要对 FSMC进行哪些配置. 这里就做一个概括性的讲解.步骤如下: 1)使能 FSMC 时钟,并配置 FSMC 相关的  ...

  8. guava cache与spring集成

    缓存的背景 缓存,在我们日常开发中是必不可少的一种解决性能问题的方法.简单的说,cache 就是为了提升系统性能而开辟的一块内存空间.在cpu进行计算的时候, 首先是读取寄存器,然后内存,再是硬盘.由 ...

  9. 测试覆盖率Emma工具使用

    Emma使用与分析 #什么是Emma EMMA 是一个开源.面向 Java 程序测试覆盖率收集和报告工具.它通过对编译后的 Java 字节码文件进行插装,在测试执行过程中收集覆盖率信息,并通过支持多种 ...

  10. Ubuntu下codeblocks编译器程序执行对话框内能进行粘贴编辑操作的指令

    如这个:  gnome-terminal  -t $TITLE -x