雨天的尾巴
题目描述
给出一棵 N个结点的树,
有 M次修改操作, 每次操作要求将 a,b之间最短路径所有点加上类型为 c的粮食 1次,
到最后输出每个点所储存的最多的粮食类型, 如果有相等数量的, 则输出类型编号最小的.
N,M,c<=105
整体使用 set 维护, 会发现由于左儿子对右儿子的影响难以消除, 于是学习了以下方法 ↓
Solution:树上差分+树链剖分+权值线段树
若只维护一个线性的序列,
假如要将 a,b之间加 c类型 1个, 即为 a位置 c种类加 1, b+1位置 c种类减 1,
对每个位置开动态数组记录 差分操作, 最后遍历时用 权值线段树 维护出现次数最多的类型即可
当其放在了 树 上时, 可以对 a,b 使用树剖中的 Modify 与 vector 标记差分操作
可以发现很神奇的一点是:
由于每条重链上的编号是连续的, 这样相当于将一次修改操作变为 logN个分段修改操作, 可以使得 dfn数组变得像线性序列一样可以一遍遍历得到答案
于是最后从 dfs序为 1开始遍历, 权值线段树扫一遍即可
复杂度 O(M∗log2N+N∗log2c)
即 O(N∗log2N)
Code
#include<bits/stdc++.h>
#define reg register
#define pb push_back
const int maxn = 100005;
int read(){
int s = 0, flag = 1;
char c;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
int N;
int M;
int num0;
int Lim;
int tim;
int Mp[maxn];
int dfn[maxn];
int top[maxn];
int max_son[maxn];
int dep[maxn];
int Fa[maxn];
int head[maxn];
int size[maxn];
int Ans[maxn];
std::vector <int> que[maxn];
struct Node{
int l, r, maxx, mp;
} T[maxn << 2];
struct Edge{
int nxt, to;
} edge[maxn << 1];
void Add(int from, int to){
edge[++ num0] = (Edge){ head[from], to };
head[from] = num0;
}
void DFS_1(int k, int fa){
size[k] = 1;
int maxx = 0;
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == fa) continue ;
DFS_1(to, k);
if(size[to] > maxx) maxx = size[to], max_son[k] = to;
size[k] += size[to];
}
}
void DFS_2(int k, int Top){ //~
dfn[k] = ++ tim;
top[k] = Top;
Mp[tim] = k;
if(max_son[k]){
Fa[max_son[k]] = k;
dep[max_son[k]] = dep[k] + 1;
DFS_2(max_son[k], Top);
}
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(dfn[to]) continue ;
dep[to] = dep[k] + 1;
Fa[to] = k;
DFS_2(to, to);
}
}
void Modify(int x, int y, int w){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]]) std::swap(x, y);
que[dfn[top[x]]].pb(w), que[dfn[x] + 1].pb(-w);
x = Fa[top[x]];
}
if(dep[x] < dep[y]) std::swap(x, y);
que[dfn[y]].pb(w), que[dfn[x] + 1].pb(-w);
}
void Build(int k, int l, int r){
T[k].l = l, T[k].r = r;
if(l == r) return ;
int mid = l+r >> 1;
Build(k<<1, l, mid), Build(k<<1|1, mid+1, r);
}
void Push_up(int k){
T[k].maxx = std::max(T[k<<1].maxx, T[k<<1|1].maxx);
Node lt = T[k<<1], rt = T[k<<1|1];
if(lt.maxx > rt.maxx) T[k].mp = lt.mp;
else if(lt.maxx == rt.maxx) T[k].mp = std::min(lt.mp, rt.mp);
else T[k].mp = rt.mp;
}
void Modify_2(int v, int opt, int k){
int l = T[k].l, r = T[k].r;
if(l == r){
T[k].maxx += opt, T[k].mp = l;
return ;
}
int mid = l+r >> 1;
if(v <= mid) Modify_2(v, opt, k<<1);
else Modify_2(v, opt, k<<1|1);
Push_up(k);
}
int main(){
N = read(), M = read();
for(reg int i = 1; i < N; i ++){
int a = read(), b = read();
Add(a, b), Add(b, a);
}
DFS_1(1, 0), DFS_2(1, 1);
for(reg int i = 1; i <= M; i ++){
int a = read(), b = read(), c = read();
Lim = std::max(Lim, c);
Modify(a, b, c);
}
Build(1, 1, Lim);
for(reg int i = 1; i <= N; i ++){
for(reg int j = 0; j < que[i].size(); j ++)
Modify_2(abs(que[i][j]), que[i][j]/abs(que[i][j]), 1);
Ans[Mp[i]] = T[1].mp;
}
for(reg int i = 1; i <= N; i ++) printf("%d\n", Ans[i]);
return 0;
}