[BZOJ3307]雨天的尾巴(LCA + 線段樹合併)
阿新 • • 發佈:2018-12-08
Address
Solution
- 首先轉化一下問題,考慮一種顏色
- 對於題目給定的一條從 到 的顏色為 的路徑
- 在節點 加上貢獻 ,節點 加上貢獻
- 在 和 的 LCA 處加上貢獻 , 和 的 LCA 的父親節點(如果存在)處加上貢獻
- 那麼容易得出,一個點 ,它子樹內所有點的貢獻之和,就等於點 被多少條顏色為 的路徑覆蓋
- 於是我們可以對於每個點 用一個 vector ,存下一些二元組
- 表示該點為顏色 貢獻了 ( 或 )
- 這時候 點的答案就是 的子樹內,貢獻最多的顏色
- 有很多種做法,這裡選用線段樹合併
- 對每個節點開一棵以顏色為下標的線段樹,儲存所有顏色的貢獻和,維護區間最大值及最大值的位置(最大值不唯一則取編號最小)
- 處理點 的答案時,先分別處理 的所有子節點的答案
- 把 的所有子節點開的線段樹合併起來(注意合併到葉子時是相加而不是取 )
- 然後在合併起來的線段樹上再加上 點的貢獻
- 那麼點 的答案就是線段樹的全域性最大值位置
- 複雜度
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;
}