[CF827D Round#423 Div.1]Best Edge Weight——[Kruskal+树链剖分]

【原题】
在这里插入图片描述
【题目翻译】

给定一个点数为 n,边数为 m,权值不超过 10^9 的带权连通图,没有自环与重边。 现在要求对于每一条边求出,这条边的边权最大为多少时,它还能出现在所有可能的最小生成树上,如果对于任意边权都出现,则输出 -1。 (2<=n<=10^5, n-1<=m<=2*10^5 )

【输入格式】

第一行两个数n,m
接下来m行每行3个数ui,vi,wi表示第i条边的两个端点及权值

【输出格式】

一行m个整数,mi表示对于第i条边的答案

S a m p l e    I n p u t Sample~~Input Sample  Input

4 4
1 2 2
2 3 2
3 4 2
4 1 3

S a m p l e    O u t p u t Sample~~Output Sample  Output

2 2 2 1

【题意分析】

这道题写得我心力交瘁

我来详细解释一下题目:对于每条边,求出最大的权值,使得这条边还可能是最小生成树上的一部分

非常绕,就是如果大过这个最大权值,这条边就不可能出现在最小生成树里。

怎么解决呢?先利用Kruskal任意求出一棵最小生成树。

接下来对于整张图中的每一条边(设左右端点为u,v):

如果它没有位于这棵最小生成树上(非树边),那么答案就是最小生成树上(u,v)这条链上的最大值减去1。

为什么?(u,v)链上的最大值是极限了,再超过这个值,这条边就大过头,没人要了。因此减去1便是答案。

同时在链上更新这条非树边的权值,维护一棵最小值的线段树(把最小生成树上(u,v)链全部对这个边的权值取min)。

对于另一种情况:在最小生成树上的边,我们更新的链上的信息就起作用了:

一条非树边,因为权值比人家大所以才选不上,所有的非树边,我们都已经对最小生成树上相对应的链修改过了(一次次取min)来约束每条树边

形象点说,最小生成树边的宝位,下面有一堆候选人在虎视眈眈,你必须比所有对你位置虎视眈眈的人都更优秀,才能够守住自己的位置。

在这里插入图片描述

举个栗子,对于树边9这个宝位,

10号非树边对8,9约束,11号非树边对7,8,9约束,13号树边对9,1,2,3约束(即最小生成树上一条链)

那么9号树边,只有14号非树边约束不到它

9号树边的权值必须比5,6,10,11,12,13,15号非树边的权值全部都要小

这便是树边的处理方式

维护方式当然选择树链剖分,带两个log完全可以过

用lct和树上倍增都可以解决,但是lct估计还跑不过树剖

注意边权化点权的处理方式

Code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#define MAXN 300000
#define INF 0x3f3f3f3f
using namespace std;

struct fls {int to, next, id, val;} edge[MAXN << 1];
struct Node {int u, v, id, val;} data[MAXN << 1];

int head[MAXN << 1], tmax[MAXN << 2], tmin[MAXN << 2], lazy[MAXN << 2], top[MAXN];
int father[MAXN], son[MAXN], size[MAXN], id[MAXN], rk[MAXN], ans[MAXN], depth[MAXN];
int pre[MAXN], len[MAXN], f[MAXN], ismst[MAXN], n, m, cnt, res, dfn;

inline int read () {
	register int s = 0, w = 1;
	register char ch = getchar ();
	while (! isdigit (ch)) {if (ch == '-') w = -1; ch = getchar ();}
	while (isdigit (ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar ();}
	return s * w;
}

inline void connect (int u, int v, int w, int i) {
	edge[++cnt].to = v, edge[cnt].val = w, edge[cnt].id = i,
	edge[cnt].next = head[u], head[u] = cnt; 
}

int getfather (int x) {return f[x] == x ? x : f[x] = getfather (f[x]);}

inline bool cmp (Node a, Node b) {return a.val < b.val;}

namespace tree_split {
	
	inline void pushup (int now) {
		tmax[now] = max (tmax[now << 1], tmax[now << 1 | 1]);
		tmin[now] = min (tmin[now << 1], tmin[now << 1 | 1]);
	}
	
	inline void pushdown (int now) {
		tmin[now << 1] = min (tmin[now << 1], lazy[now]);
		lazy[now << 1] = min (lazy[now << 1], lazy[now]);
		tmin[now << 1 | 1] = min (tmin[now << 1 | 1], lazy[now]);
		lazy[now << 1 | 1] = min (lazy[now << 1 | 1], lazy[now]);
		lazy[now] = INF;
	}
	
	void build (int now, int l, int r) {
		tmin[now] = lazy[now] = INF;
		if (l == r) {
			tmax[now] = rk[l]; return;
		}
		int mid = l + r >> 1;
		build (now << 1, l, mid);
		build (now << 1 | 1, mid + 1, r);
		pushup (now);
	}
	
	void updateMin (int now, int l, int r, int L, int R, int k) {
		if (R < l || r < L) return;
		if (L <= l && r <= R) {
			tmin[now] = min (tmin[now], k);
			lazy[now] = min (lazy[now], k);
			return;
		}
		pushdown (now);
		int mid = l + r >> 1;
		updateMin (now << 1, l, mid, L, R, k);
		updateMin (now << 1 | 1, mid + 1, r, L, R, k);
		pushup (now);
	}
	
	void queryMax (int now, int l, int r, int L, int R) {
		if (R < l || r < L) return;
		if (L <= l && r <= R) {
			res = max (res, tmax[now]); return;
		}
		pushdown (now);
		int mid = l + r >> 1;
		queryMax (now << 1, l, mid, L, R);
		queryMax (now << 1 | 1, mid + 1, r, L, R);
	}
	
	void queryMin (int now, int l, int r, int L, int R) {
		if (R < l || r < L) return;
		if (L <= l && r <= R) {
			res = min (res, tmin[now]); return;
		}
		pushdown (now);
		int mid = l + r >> 1;
		queryMin (now << 1, l, mid, L, R);
		queryMin (now << 1 | 1, mid + 1, r, L, R);
	}
	
	inline void MRMin (int x, int y, int k) {
		while (top[x] != top[y]) {
			if (depth[top[x]] < depth[top[y]]) swap (x, y);
			updateMin (1, 1, n, id[top[x]], id[x], k);
			x = father[top[x]];
		}
		if (depth[x] > depth[y]) swap (x, y);
		updateMin (1, 1, n, id[x] + 1, id[y], k);
	}
	
	inline int QRMax (int x, int y) {
		int tmp = -1;
		while (top[x] != top[y]) {
			if (depth[top[x]] < depth[top[y]]) swap (x, y);
			res = -1; queryMax (1, 1, n, id[top[x]], id[x]);
			tmp = max (tmp, res), x = father[top[x]];
		}
		if (depth[x] > depth[y]) swap (x, y);
		res = -1; queryMax (1, 1, n, id[x] + 1, id[y]);
		return max (tmp, res);
	}
	
	void DFS1 (int now, int fa, int d) {
		depth[now] = d, father[now] = fa, size[now] = 1;
		int maxson = -1;
		for (register int i = head[now]; i; i = edge[i].next) {
			int v = edge[i].to;
			if (v == fa) continue;
			pre[v] = edge[i].id, len[v] = edge[i].val;
			DFS1 (v, now, d + 1);
			size[now] += size[v];
			if (size[v] > maxson) {
				maxson = size[v];
				son[now] = v;
			}
		}
	}
	
	void DFS2 (int now, int top_heavy) {
		top[now] = top_heavy;
		id[now] = ++dfn, rk[dfn] = len[now];
		if (! son[now]) return;
		DFS2 (son[now], top_heavy);
		for (register int i = head[now]; i; i = edge[i].next) {
			int v = edge[i].to;
			if (v != son[now] && v != father[now]) DFS2 (v, v);
		}
	}
	
}using namespace tree_split;

int main () {
	n = read (), m = read ();
	for (register int i = 1; i <= m; i++)
		data[i].u = read (), data[i].v = read (),
		data[i].val = read (), data[i].id = i;
	sort (data + 1, data + m + 1, cmp);
	for (register int i = 1; i <= n; i++) f[i] = i;
	for (register int i = 1; i <= m; i++) {
		int xx = getfather (data[i].u), yy = getfather (data[i].v);
		if (xx != yy) {
			f[yy] = xx, ismst[i] = 1;
			connect (data[i].u, data[i].v, data[i].val, data[i].id);
			connect (data[i].v, data[i].u, data[i].val, data[i].id);
		}
	}
	DFS1 (1, 0, 1), DFS2 (1, 1), build (1, 1, n);
	for (register int i = 1; i <= m; i++)
		if (! ismst[i]) {
			ans[data[i].id] = QRMax (data[i].u, data[i].v) - 1;
			MRMin (data[i].u, data[i].v, data[i].val);
		}
	for (register int i = 2; i <= n; i++) {
		res = INF; queryMin (1, 1, n, id[i], id[i]);
		ans[pre[i]] = res - 1;
	}
	for (register int i = 1; i <= m; i++)
		if (ans[i] < INF - 1) printf ("%d ", ans[i]);
		else printf ("%d ", -1);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值