浏览器标题切换
浏览器标题切换end
把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

BZOJ3319: 黑白树

Description

给定一棵树,边的颜色为黑或白,初始时全部为白色。维护两个操作:

1.查询u到根路径上的第一条黑色边的标号。

2.将u到v 路径上的所有边的颜色设为黑色。

Notice:这棵树的根节点为1

Input

第一行两个数n,m分别表示点数和操作数。

接下来n-1行,每行2个数u,v.表示一条u到v的边。

接下来m行,每行为以下格式:

1 v 表示第一个操作

2 v u 表示第二种操作

n,m<=10^6

Output

对于每个询问,输出相应答案。如果不存在,输出0

Sample Input

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

Sample Output

0
2
1

Solution

一开始只会 \(O(n\log ^2n)\) 的树剖......看到\(n, m\le 10^6\)果断没写。
被zn提示了一下把染色理解成删边,然后我再回忆到了\(BZOJ4551\)那题的并查集做法,就有了大概的思路......
离线处理。
考虑全部染完后的树,把那些自始至终都没有被染过色的边并查集并一下并到\(dep\)最小的点为根(这个可以让每个点\(merge\)自己的父亲),就可以\(O(\log n)\)查询每个点向上到根第一条黑边所在的位置(每个点代表它连向父亲的边)。
处理出这个染完的树需要一定的技巧...首先可以再开一个并查集,因为每条边是只需要被访问一次(即使它可能被染色多次,我们要的是第一次染色的时间),所以可以在新的并查集里面把这段全都\(merge\)起来,相当于压缩了这段路径。然后利用邻接表或者\(vector\)将这段操作历经的所有点的边编号记录下来(以便后面处理)。这样处理的复杂度就是\(O(n\log n)\)的了。
倒着处理所有操作,对于每个查询操作按前面提到的做法在并查集里面查一下即可。对于染色操作,将邻接表/\(vector\)的边都取出来,将这条边的端点\(merge\)起来,就可以保证在处理查询操作时查到的是最近的黑色边了。
细节还是不是很会写......参详了这篇博客的代码......
总的时间复杂度是\(O(n \log n)\)。如果采用\(\text{tarjan LCA}\)并且并查集按秩合并+路径压缩的话可以做到\(O(n + q)\)(众所周知加了那俩优化的并查集近似线性复杂度了...)

#include <bits/stdc++.h>
using namespace std;

namespace io {
char buf[1<<21], *p1 = buf, *p2 = buf;
inline char gc() {
    if(p1 != p2) return *p1++;
    p1 = buf;
    p2 = p1 + fread(buf, 1, 1 << 21, stdin);
    return p1 == p2 ? EOF : *p1++;
}
#define G gc

#ifndef ONLINE_JUDGE
#undef G
#define G getchar
#endif

template<class I>
inline void read(I &x) {
    x = 0; I f = 1; char c = G();
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = G(); }
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = G(); }
    x *= f;
}
template<class I>
inline void write(I x) {
    if(x == 0) {putchar('0'); return;}
    I tmp = x > 0 ? x : -x;
    if(x < 0) putchar('-');
    int cnt = 0;
    while(tmp > 0) {
        buf[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while(cnt > 0) putchar(buf[--cnt]);
}

#define in(x) read(x)
#define outn(x) write(x), putchar('\n')
#define out(x) write(x), putchar(' ')

#undef G

} using namespace io;

#define ll long long
const int N = 1000100;

int n, m, dep[N], f[N][22], id[N], Del[N], ans[N];
struct B {
	int f[N];
	void init() {for(int i = 1; i <= n; ++i) f[i] = i;}
	int find(int x) {if(f[x] == x) return x; return f[x] = find(f[x]);}
	void merge(int x, int y) {
		x = find(x); y = find(y);
		if(dep[x] < dep[y]) swap(x, y); 
		f[x] = y;
	}
} F, tmp;
struct Edge {
	int head[N], cnt;
	struct edge {int to, nxt, from, id;} e[N << 1];
	void ins(int u, int v, int id) {
		e[++cnt] = (edge) {v, head[u], u, id};
		head[u] = cnt;
	}
} G, g;
struct Q {
	int x, y, id, LCA, op;
} q[N];

void dfs(int u) {
	for(int i = G.head[u]; i; i = G.e[i].nxt) {
		int v = G.e[i].to;
		if(v == f[u][0]) continue;
		f[v][0] = u;
		for(int j = 1; j <= 20; ++j) f[v][j] = f[f[v][j - 1]][j - 1];
		dep[v] = dep[u] + 1;
		id[v] = i;
		dfs(v);
	}
}

int lca(int x, int y) {
	if(dep[x] < dep[y]) swap(x, y);
	for(int i = 20; i >= 0; --i) 
		if(dep[f[x][i]] >= dep[y]) x = f[x][i];
	if(x == y) return x;
	for(int i = 20; i >= 0; --i) 
		if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	return f[x][0];
}

void cut(int x, int y, int l, int now) {
	for(x = tmp.find(x); dep[x] > dep[l]; Del[x] = 1, tmp.merge(x, f[x][0]), g.ins(now, id[x], 1), x = tmp.find(f[x][0]));
	for(y = tmp.find(y); dep[y] > dep[l]; Del[y] = 1, tmp.merge(y, f[y][0]), g.ins(now, id[y], 1), y = tmp.find(f[y][0]));
}
void link(int now) {
	for(int i = g.head[now]; i; i = g.e[i].nxt) F.merge(G.e[g.e[i].to].to, G.e[g.e[i].to].from);
}

int main() {
	read(n); read(m);
	for(int x, y, i = 1; i < n; ++i) {read(x); read(y); G.ins(x, y, i); G.ins(y, x, i);}
	dfs(1); tmp.init(); F.init();
	for(int i = 1; i <= m; ++i) {
		read(q[i].op); read(q[i].x); q[i].id = i;
		if(q[i].op == 2) {
			read(q[i].y), q[i].LCA = lca(q[i].x, q[i].y);
			cut(q[i].x, q[i].y, q[i].LCA, i);
		}
	}
	for(int i = 2; i <= n; ++i) if(!Del[i]) F.merge(i, f[i][0]);
	int tot = 0;
	for(int i = m; i; --i) {
		if(q[i].op == 2) link(q[i].id);
		else {
			int now = F.find(q[i].x);
			if(now == 1) ans[++tot] = 0;
			else ans[++tot] = G.e[id[now]].id;
		}
	}
	for(int i = tot; i; --i) outn(ans[i]);
}
posted @ 2019-08-02 22:48  henry_y  阅读(356)  评论(0编辑  收藏  举报