1. 程式人生 > >[BZOJ3307]雨天的尾巴(LCA + 線段樹合併)

[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;
}