[BZOJ3307]雨天的尾巴(LCA + 线段树合并)

Address

Solution

  • 首先转化一下问题,考虑一种颜色 c c
  • 对于题目给定的一条从 u u v v 的颜色为 c c 的路径
  • 在节点 u u 加上贡献 1 1 ,节点 v v 加上贡献 1 1
  • u u v v 的 LCA 处加上贡献 1 -1 u u v v 的 LCA 的父亲节点(如果存在)处加上贡献 1 -1
  • 那么容易得出,一个点 u u ,它子树内所有点的贡献之和,就等于点 u u 被多少条颜色为 c c 的路径覆盖
  • 于是我们可以对于每个点 u u 用一个 vector ,存下一些二元组 ( c , d e l t a ) (c,delta)
  • 表示该点为颜色 c c 贡献了 d e l t a delta d e l t a = 1 delta=1 1 -1
  • 这时候 u u 点的答案就是 u u 的子树内,贡献最多的颜色
  • 有很多种做法,这里选用线段树合并
  • 对每个节点开一棵以颜色为下标的线段树,储存所有颜色的贡献和,维护区间最大值及最大值的位置(最大值不唯一则取编号最小)
  • 处理点 u u 的答案时,先分别处理 u u 的所有子节点的答案
  • u u 的所有子节点开的线段树合并起来(注意合并到叶子时是相加而不是取 max \max
  • 然后在合并起来的线段树上再加上 u u 点的贡献
  • 那么点 u u 的答案就是线段树的全局最大值位置
  • 复杂度 O ( n log n ) O(n\log n)

Code

常数巨大,开了 O2 跑了 395ms

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Tree(u) for (int e = adj[u], v; e; e = nxt[e]) if ((v = go[e]) != fu)

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

template <class T>
inline void Swap(T &a, T &b) {a ^= b; b ^= a; a ^= b;}

const int MAX = 1e5, N = 1e5 + 5, M = N << 1, L = 4e6 + 5, LogN = 20;

int n, m, ecnt, nxt[M], adj[N], go[M], dep[N], ans[N], fa[N][LogN], ToT;

std::vector<int> xin[N], xout[N];

struct node
{
	int lc, rc, pos, maxv;
} T[L];

void add_edge(int u, int v)
{
	nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
	nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}

void dfs(int u, int fu)
{
	int i;
	dep[u] = dep[fa[u][0] = fu] + 1;
	For (i, 0, 15) fa[u][i + 1] = fa[fa[u][i]][i];
	Tree(u) dfs(v, u);
}

int lca(int u, int v)
{
	if (dep[u] < dep[v]) Swap(u, v);
	int i;
	Rof (i, 16, 0)
	{
		if (dep[fa[u][i]] >= dep[v]) u = fa[u][i];
		if (u == v) return u;
	}
	Rof (i, 16, 0)
		if (fa[u][i] != fa[v][i])
			u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}

int mer(int l, int r, int x, int y)
{
	if (!x || !y) return x ^ y;
	int mid = l + r >> 1;
	if (l == r)
	{
		T[x].maxv += T[y].maxv;
		T[x].pos = T[x].maxv ? l : 0;
		return x;
	}
	T[x].lc = mer(l, mid, T[x].lc, T[y].lc);
	T[x].rc = mer(mid + 1, r, T[x].rc, T[y].rc);
	if (T[T[x].lc].maxv > T[T[x].rc].maxv || (T[T[x].lc].maxv
		== T[T[x].rc].maxv && T[T[x].lc].pos < T[T[x].rc].pos))
			T[x].maxv = T[T[x].lc].maxv, T[x].pos = T[T[x].lc].pos;
	else T[x].maxv = T[T[x].rc].maxv, T[x].pos = T[T[x].rc].pos;
	return x;
}

void change(int l, int r, int pos, int v, int &p)
{
	if (!p) p = ++ToT;
	if (l == r)
	{
		if (T[p].maxv += v) T[p].pos = l;
		return;
	}
	int mid = l + r >> 1;
	if (pos <= mid) change(l, mid, pos, v, T[p].lc);
	else change(mid + 1, r, pos, v, T[p].rc);
	if (T[T[p].lc].maxv > T[T[p].rc].maxv || (T[T[p].lc].maxv
		== T[T[p].rc].maxv && T[T[p].lc].pos < T[T[p].rc].pos))
			T[p].maxv = T[T[p].lc].maxv, T[p].pos = T[T[p].lc].pos;
	else T[p].maxv = T[T[p].rc].maxv, T[p].pos = T[T[p].rc].pos;
}

int jiejuediao(int u, int fu)
{
	int i, tmp, rt = 0;
	Tree(u) rt = mer(1, MAX, rt, jiejuediao(v, u));
	tmp = xin[u].size();
	For (i, 0, tmp - 1) change(1, MAX, xin[u][i], 1, rt);
	tmp = xout[u].size();
	For (i, 0, tmp - 1) change(1, MAX, xout[u][i], -1, rt);
	return ans[u] = T[rt].pos, rt;
}

int main()
{
	int i, x, y, z;
	n = read(); m = read();
	For (i, 1, n - 1) x = read(), y = read(), add_edge(x, y);
	dfs(1, 0);
	while (m--)
	{
		x = read(); y = read(); z = read();
		int w = lca(x, y);
		xin[x].push_back(z);
		xin[y].push_back(z);
		xout[w].push_back(z);
		if (w > 1) xout[fa[w][0]].push_back(z);
	}
	jiejuediao(1, 0);
	For (i, 1, n) printf("%d\n", ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/84892358