F. MST Unification
https://codeforces.com/contest/1108/problem/F
题意
n个点,m条边,每条边都有一个权值,每次操作可以使得一条边的权值减1,问最小需要几次操作,使得最小生成树唯一。
题解
对于最小生成树不唯一的情况就是,添加一条非树边后(u,v,w)后构成的环中的最小值(除非树边的最小值)等于非树边,这样就可以用非树边去替代这条边使得最小生成树不唯一,所以先构造一棵最小生成树,然后只需要对每条非树边(u,v,w)查询树边u到v中的最小值是否等于w,若等于则需要的操作+1。
这里查找树上(u,v)之间的最小值采用的是倍增LCA的方法。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
const int maxn = 2e5+5;
int _max[maxn][20];
int dep[maxn];
struct Edge{
int v,c;
Edge(int _v,int _c):v(_v),c(_c){}
};
vector<Edge> G[maxn];
int n,m;
void add(int f,int v,int c) {
G[f].push_back(Edge(v,c));
}
int p[maxn][20];
void dfs(int u,int fa) {
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i].v;
if(v == fa) continue;
_max[v][0] = G[u][i].c;
dep[v] = dep[u]+1;
p[v][0] = u;
dfs(v,u);
}
}
void initLCA() {
for(int i = 1; i <= 18; ++i)
for(int j = 1; j <= n; ++j) {
p[j][i] = p[p[j][i-1]][i-1];
_max[j][i] = max(_max[j][i-1],_max[p[j][i-1]][i-1]);
}
}
int LCA(int x,int y) {
if(dep[x] < dep[y])
swap(x,y);
int ans = 0;
for(int i = 18; i >= 0; --i)
if(dep[p[x][i]] >= dep[y]) {
ans = max(ans, _max[x][i]);
x = p[x][i];
}
if(x == y)
return ans;
for(int i = 18; i >= 0; --i) {
if(p[x][i] != p[y][i]) {
ans = max(ans, _max[x][i]);
ans = max(ans, _max[y][i]);
x = p[x][i];
y = p[y][i];
}
}
return max(ans,max(_max[x][0],_max[y][0]));
}
struct Edge2{
int u,v,w;
bool operator < (const Edge2& rhs) const {
return w < rhs.w;
}
}eg[maxn];
int pre[maxn];
bool flag[maxn];
int find(int x) {
return x == pre[x] ? x : pre[x] = find(pre[x]);
}
void solve() {
int cnt = 0;
for(int i = 0; i < m; ++i)
if(!flag[i]) {
int u = eg[i].u, v = eg[i].v;
// cout << u <<" " << v << " " << LCA(u,v) << endl;
if(LCA(u,v) == eg[i].w)
cnt++;
}
cout << cnt << endl;
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 0; i < m; ++i) {
scanf("%d%d%d", &eg[i].u, &eg[i].v, &eg[i].w);
}
for(int i = 0; i <= n; ++i) pre[i] = i;
sort(eg,eg+m);
for(int i = 0; i < m; ++i) {
int uu = find(eg[i].u), vv = find(eg[i].v);
if(uu != vv) {
flag[i] = 1;
add(eg[i].u,eg[i].v,eg[i].w);
add(eg[i].v,eg[i].u,eg[i].w);
pre[uu] = vv;
}
}
dep[1] = 1;
dfs(1,-1);
initLCA();
solve();
return 0;
}
D. Best Edge Weight
https://codeforces.com/problemset/problem/827/D
题意
n个点,m条边,每条边都有一个权值w,问如果这条边在最小生成树上,那么w最大可以是多少?
题解
这和上题差不多,先建一个最小生成树
- 对于非树边(u,v)的最大值就是树边上(u,v)的最大值减1。因为对于端点为(u,v)的环,非树边只需要比其中的最大值小1就可以替换一条边。
- 对于树边(u’,v’)的最大值就是非树边上(u’,v’)上的最小值减1。如果连上非树边(u’,v’)后构成一个环,那如果这条树边
对于树边上的最大值可以预处理出来,但对于非树边的最小值就需要直接在树上更新。更新的步骤相当于求倍增LCA的逆过程,先按照找LCA的步骤把大区间的最小值更新一下,然后最后再从大的区间更新到小的区间。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 2e5+5;
const int INF = 0x3f3f3f3f;
struct Edge{
int u,v,w,id;
Edge(){}
Edge(int _u, int _v, int _w) :u(_u),v(_v),w(_w){}
bool operator < (const Edge& rhs) const {
return w < rhs.w;
}
};
Edge eg[maxn];
int n,m,pre[maxn],dep[maxn],mx[maxn][20],p[maxn][20], _min[maxn][20];
vector<Edge> G[maxn];
bool flag[maxn];
void add(int u, int v, int w) {
G[u].push_back(Edge(u,v,w));
}
int find(int x) {
return x == pre[x] ? x : pre[x] = find(pre[x]);
}
void dfs(int u,int fa) {
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i].v;
if(v == fa)
continue;
dep[v] = dep[u]+1;
p[v][0] = u;
mx[v][0] = G[u][i].w;
dfs(v,u);
}
}
void initLCA() {
for(int i = 1; i <= 18; ++i)
for(int j = 1; j <= n; ++j) {
p[j][i] = p[p[j][i-1]][i-1];
mx[j][i] = max(mx[j][i-1],mx[p[j][i-1]][i-1]);
}
}
int LCA(int a, int b) {
if(dep[a] < dep[b])
swap(a,b);
int ans = 0;
for(int i = 18; i >=0; --i) {
if(dep[p[a][i]] >= dep[b]) {
ans = max(ans, mx[a][i]);
a = p[a][i];
}
}
if(a == b)
return ans;
for(int i = 18; i >= 0; --i)
if(p[a][i] != p[b][i]) {
ans = max(ans, mx[a][i]);
ans = max(ans, mx[b][i]);
a = p[a][i];
b = p[b][i];
}
return max(ans, max(mx[a][0],mx[b][0]));
}
void update(int x, int y, int val) {
if(dep[x] < dep[y])
swap(x,y);
for(int i = 18; i >= 0; --i) {
if(dep[p[x][i]] >= dep[y]) {
_min[x][i] = min(_min[x][i], val);
x = p[x][i];
}
}
if(x == y)
return;
for(int i = 18; i >= 0; --i) {
if(p[x][i] != p[y][i]) {
_min[x][i] = min(_min[x][i], val);
_min[y][i] = min(_min[y][i], val);
x = p[x][i];
y = p[y][i];
}
}
_min[x][0] = min(_min[x][0], val);
_min[y][0] = min(_min[y][0], val);
}
int ans[maxn];
void solve() {
memset(ans, INF, sizeof ans);
memset(_min, INF, sizeof _min);
for(int i = 0; i < m; ++i) {
if(!flag[i]) {
ans[eg[i].id] = LCA(eg[i].u, eg[i].v)-1;
update(eg[i].u, eg[i].v,eg[i].w-1);
}
}
for(int i = 17; i >= 0; --i)
for(int j = 1; j <= n; ++j) {
_min[j][i] = min(_min[j][i],_min[j][i+1]);
_min[p[j][i]][i] = min(_min[p[j][i]][i], _min[j][i+1]);
}
for(int i = 0; i < m; ++i) {
if(flag[i]) {
int u = eg[i].u, v = eg[i].v;
if(p[u][0] == v) {
ans[eg[i].id] = _min[u][0];
}
else
ans[eg[i].id] = _min[v][0];
}
}
for(int i = 0; i < m; ++i)
if(ans[i] == INF)
cout << -1 << " ";
else
cout << ans[i] << " ";
puts("");
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 0; i < m; ++i) {
scanf("%d%d%d", &eg[i].u, &eg[i].v, &eg[i].w);
eg[i].id = i;
}
sort(eg,eg+m);
for(int i = 0; i <= n; ++i)
pre[i] = i;
for(int i = 0; i < m; ++i) {
int u = eg[i].u, v = eg[i].v;
int uu = find(u), vv = find(v);
if(uu!= vv) {
flag[i] = 1;
pre[uu] = vv;
add(u,v,eg[i].w);
add(v,u,eg[i].w);
}
}
dep[1] = 1;
dfs(1,-1);
initLCA();
solve();
return 0;
}
3707



被折叠的 条评论
为什么被折叠?



