loj3885. 「eJOI2022」Bounded Spanning Tree

对于非树边 \(u,v,[l,r]\)\(u,v\) 路径上所有树边边权必然 \(<r\)。先修改路径上这些边边权的上界。

可以想到这样一种贪心策略,先考虑树边,如果发现一条路径 \(u,v\) 上的树边全部设定好边权了,那么把非树边 \(u,v\) 纳入考虑范围。

具体的,把所有树边按边权下界排序,按照 \(i=1~m\) 的顺序算出哪条边的边权应该是 \(i\)。如果 \(m=n-1\) 显然用个堆随便乱贪。

如果当前堆取出来一条树边,合并 \(u,v\),看一下有哪些非树边满足条件,如果非树边的边权下界 \(\le i\) 就直接扔堆里,否则插入到边权下界对应的 vector 里,扫到了再扔进去。

找这些非树边可以线段树合并。对于第 \(i\) 条非树边 \(u,v\),在 \(u,v\) 的线段树里插入 \(i\)。线段树合并时发现两颗线段树的公有位置就是需要纳入考虑范围的非树边。

正确性:刚刚修改路径上树边上界时,其实有个镜像的过程是修改这条非树边的下界(把它的下界改为路径上所有下界的 \(\max+1\))。所以非树边一定是要在这条路径上的边边权定下来过后再考虑的,这也符合那个下界排序的贪心策略。

也可以 unordered_set 启发式合并。

给个线段树合并的代码:

#include <cstdio>
#include <queue>
#include <numeric>
#include <algorithm>
#include <cstring>
#include <unordered_map>
#define gc (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++)
#define pc(ch) (pw < 100000 ? obuf[pw ++] = (ch) : (fwrite(obuf, 1, 100000, stdout), obuf[(pw = 0)++] = (ch)))

char buf[100000], *p1, *p2, obuf[100000];
int pw;

inline int read() {
	char ch;
	int x = 0;
	while ((ch = gc) < 48);
	do x = x * 10 + ch - 48; while ((ch = gc) >= 48);
	return x;
}
inline void write(int val) {
	static char a[10];
	int len = 0;
    do a[++ len] = val % 10 + '0', val /= 10; while (val);
    while (len) pc(a[len --]);
    return;
}
struct Edge {int to, nxt, id;} e[1000005];
struct node {
	int u, v, l, r, id;
	inline bool operator < (const node a) const {return r > a.r;}
} a[500005];
int head[500005], f[500005], fa[500005], sze[500005], son[500005], id[500005], dep[500005], top[500005], tot, n, m;
int root[500005], ls[20000005], rs[20000005], common[500005], ans[500005], len, cnt;
std::priority_queue<node> Q;
std::vector<int> vec[500005];
int find(int x) {return f[x] == x ? x : f[x] = find(f[x]);}
inline void AddEdge(int u, int v, int id) {
	e[++ tot].to = v, e[tot].id = id, e[tot].nxt = head[u], head[u] = tot;
}
void dfs1(int u) {
	dep[u] = dep[fa[u]] + 1, sze[u] = 1, son[u] = 0;
	for (int i = head[u]; i; i = e[i].nxt)
		if (e[i].to != fa[u]) {
			int v = e[i].to;
			fa[v] = u, id[v] = e[i].id, dfs1(v), sze[u] += sze[v];
			if (sze[v] > sze[son[u]]) son[u] = v;
		}
}
void dfs2(int u) {
	if (son[u]) top[son[u]] = top[u], dfs2(son[u]);
	for (int i = head[u]; i; i = e[i].nxt)
		if (e[i].to != fa[u] && e[i].to != son[u]) top[e[i].to] = e[i].to, dfs2(e[i].to);
}
int LCA(int u, int v) {
	while (top[u] != top[v]) {
	    if (dep[top[u]] > dep[top[v]]) u = fa[top[u]];
	    else v = fa[top[v]];
	}
	return dep[u] < dep[v] ? u : v;
}
void insert(int &p, int l, int r, int x) {
	if (!p) ls[p = ++ cnt] = 0, rs[p] = 0;
	if (l == r) return;
	int mid = l + r >> 1;
	x <= mid ? insert(ls[p], l, mid, x) : insert(rs[p], mid + 1, r, x);
}
void merge(int &u, int v, int l, int r) {
	if (!u || !v) return u |= v, void();
	if (l == r) return common[++ len] = l + n - 1, u = 0, void();
	int mid = l + r >> 1;
	merge(ls[u], ls[v], l, mid), merge(rs[u], rs[v], mid + 1, r);
	if (!ls[u] && !rs[u]) u = 0;
}
void solve() {
	n = read(), m = read();
	tot = cnt = 0;
	for (int i = 1; i <= n; ++ i) f[i] = i, head[i] = root[i] = 0;
	for (int i = 1; i <= m; ++ i)
		a[i].u = read(), a[i].v = read(), a[i].l = read(), a[i].r = read(), a[i].id = i, vec[i].clear();
	for (int i = 1; i < n; ++ i) AddEdge(a[i].u, a[i].v, i), AddEdge(a[i].v, a[i].u, i);
	std::sort(a + n, a + m + 1), dfs1(1), top[1] = 1, dfs2(1);
	for (int i = m; i >= n; -- i) {
		int u = find(a[i].u), v = find(a[i].v), lca = LCA(a[i].u, a[i].v);
		while (dep[u] > dep[lca])
			a[id[u]].r = std::min(a[id[u]].r, a[i].r - 1), f[u] = find(fa[u]), u = f[u];
		while (dep[v] > dep[lca])
			a[id[v]].r = std::min(a[id[v]].r, a[i].r - 1), f[v] = find(fa[v]), v = f[v];
		insert(root[a[i].u], 1, m - n + 1, i - n + 1), insert(root[a[i].v], 1, m - n + 1, i - n + 1);
	}
	std::iota(f + 1, f + n + 1, 1);
	for (int i = 1; i < n; ++ i) vec[a[i].l].push_back(i);
	bool flag = true;
	for (int i = 1; i <= m; ++ i) {
		for (int j : vec[i]) Q.push(a[j]);
		if (Q.empty()) {flag = false; break;}
		node t = Q.top();
		Q.pop();
		if (t.r < i) {flag = false; break;}
		ans[t.id] = i;
		if (t.id < n) {
			merge(root[find(t.u)], root[find(t.v)], 1, m - n + 1), f[f[t.v]] = f[t.u];
			while (len) {
				if (a[common[len]].l <= i + 1) Q.push(a[common[len]]);
				else vec[a[common[len]].l].push_back(common[len]);
				-- len;
			}
		}
	}
	while (Q.size()) Q.pop();
	if (!flag) return pc('N'), pc('O'), pc('\n'), void();
	pc('Y'), pc('E'), pc('S'), pc('\n');
	for (int i = 1; i <= m; ++ i) write(ans[i]), pc(' ');
	pc('\n');
}

int main() {
	int _ = read();
	while (_ --) solve();
	return fwrite(obuf, 1, pw, stdout), 0;
}
posted @ 2022-10-22 20:49  zqs2020  阅读(74)  评论(0编辑  收藏  举报