給定一棵
n 個節點的有根樹,節點編號1到
n ,根節點為
1號點。每個節點要麼是黑色要麼是白色,並且有權值wi
,初始時所有節點都是白色的。現在總共有
m 個操作,分以下兩種:
∙將節點
v 的顏色修改為黑色
∙ 給定
v ,要求找到乙個黑色節點
u,最大化wl
ca(u
,v) 。如果此時樹上不存在黑色節點,輸出−1
。 1≤
n≤105
,1≤m
≤2×10
5 我們可以將每個點的貢獻分開來考慮。
顯然點x
只會對其子樹內的節點造成貢獻。假設我們要修改點
x為黑色節點,那麼我們首先要將
x 的子樹內所有點答案都與wx
取max
,因為它們都可以與
x 組合。然後我們還要從
x的父親開始依次列舉
x 的祖先
f,顯然
f 的整棵子樹除去
x所在那棵兒子的子樹的所有點都可以與
x 組合,得到答案wf
,那麼我們將wf
與這些點的答案取
max 。
但是我們是否真的要列舉
x 的所有祖先呢?其實不然,可以發現,如果祖先
f之前已經被更新過一次,那麼其到根路徑上所有點在這次更新將會執行的操作其實和之前執行那一次是一樣的,我們不必重複,直接退出即可。
這樣乙個點最多隻會被修改兩次。至於每個點的答案值使用線段樹+df
s 序維護
max 標記即可。
時間複雜度o(
nlogn+
mlogn)
。
#include
#include
#include
#include
using
namespace
std;
int read()
int buf[30];
void write(int x)
const
int n=100050;
const
int e=n<<1;
int fa[n],w[n],last[n],dfn[n],size[n];
bool black[n],vis[n];
int tov[e],nxt[e];
int n,m,tot,idx;
struct segment_tree
int query(int x,int y,int l,int r)
void modify(int x,int st,int en,int l,int r,int delta)
int mid=l+r>>1;
if (en<=mid) modify(x<<1,st,en,l,mid,delta);
else
if (mid+1
<=st) modify(x<<1|1,st,en,mid+1,r,delta);
else modify(x<<1,st,mid,l,mid,delta),modify(x<<1|1,mid+1,en,mid+1,r,delta);
}void build(int x,int l,int r)
}t;void insert(int x,int y)
void dfs(int x)
int ask(int x)
void change(int x)
}int main()
fclose(stdin),fclose(stdout);
return
0;}
Jzoj4888 最近公共祖先
yjc最近在學習樹的有關知識。今天,他遇到了這麼乙個概念 最近公共祖先。對於有根樹t的兩個結點u v,最近公共祖先lca t,u,v 表示乙個結點x,滿足x是u v的祖先且x的深度盡可能大。yjc很聰明,他很快就學會了如何求最近公共祖先。他現在想尋找最近公共祖先有什麼性質,於是他提出了這樣的乙個問題...
最近公共祖先 python 最近公共祖先
lca演算法樸素演算法 也就是我們所說的暴力演算法,大致的思路是從樹根開始,往下迭代,如果當前結點比兩個結點都小,那麼說明要從樹的右子樹中找 相反則從左子樹中查詢 直到找到乙個結點在當前結點的左邊,乙個在右邊,說明當前結點為最近公共祖先,如果乙個結點是另外乙個結點的祖先,那麼返回前面結點的父親結點即...
最近公共祖先 LCA 最近公共祖先
直接暴力搜尋參考 普通搜尋每次查詢都需要 樸素演算法是一層一層往上找,倍增的話直接預處理出乙個 具體做法是 維護乙個 的關係來線性求出這個陣列 int anc n 31 int dep n 記錄節點深度 void dfs int u,int parent for int i 0 i g u size...