看原題戳這兒
如題,肯定是樹鏈剖分的題。
建議先a掉這道模板題(不會的先看這個)
前置知識:鏈式前向星,樹,dfs序,lca,樹形dp,線段樹,樹鏈剖分\(······\)
一定要先完全學懂,否則不保證這篇題解能完全看懂!!!
先簡化題目:
已知一棵包含 \(n(0 \le n \le 100000)\) 個結點的樹(連通且無環),每條邊上包含乙個數值,需要支援以下操作:
瞄一眼資料,\(n\)最大是\(^5\),肯定要用\(o(nlogn)\)的演算法,樹剖和線段樹剛好合適。
這道題雖然說已經確定下來要用樹鏈剖分來做了,但是樹鏈剖分和線段樹不都是求點嗎?怎麼可以用來求邊呢?這就是這道題目的毒瘤之處。
那應該怎麼做呢?
如下圖所示,把每邊權值賦給其下端的點(根節點為0)
然後就是模板的問題了
但有幾個細節,本題又加又覆,需要兩個懶標記,記得處理他們的關係
還有一件事,當查詢時轉化為一條重鏈時,如圖:
此時我們要求\(u,v\)兩點間的權值最大值,但是\(u\)節點存的是紅邊的值,這不是我們要找的答案
而\(sun_u\)存的是藍邊,這正是我們的目標
所以最後要查詢的區間是\(son_u\) 與 \(v\)之間的最大值
這個dfs要處理這四件事情:
標記每個點的深度dep[ ]
標記每個點的父親fa[ ]
標記每個非葉子節點的子樹大小(含它自己)
標記每個非葉子節點的重兒子編號son[ ]
void dfs1(int u,int fa)//u表示當前節點,fa表示當前節點的父親
}
這個dfs要處理這四件事情:
標記每個點的新編號
賦值每個點的初始值到新編號上
處理每個點所在鏈的頂端
處理每條鏈
順序:先處理重兒子再處理輕兒子
為什麼要先處理重兒子?
答:這樣在後面我們所要處理的所有區間中點的編號(新編號)均為連續的,這樣方便我們建線段樹。
(其實就和線段樹一樣)
前面說到要用線段樹,那麼按題意建樹就可以了。
不過需要注意的是,建樹這一步要放在處理問題之前,不然\(······\)。
方法:不停執行這兩個步驟,直到兩個點處於一條鏈上,這時再加上此時兩個點的區間和即可
這時我們注意到,我們所要處理的所有區間均為連續編號(新編號),於是想到線段樹,用線段樹處理連續編號區間和(為什麼**這麼長?就因為這兒)
每次查詢時間複雜度為\(o(log^2n)\),不錯。
void tupdate(int a,int b,int c,int flag)//樹剖區間修改
想到要記錄每個非葉子節點的子樹大小(含它自己),並且每個子樹的新編號都是連續的,然後直接線段樹區間查詢就行啦!!!時間複雜度為$ o(logn) $,很好
inline int qson(int x)
當然,區間修改是和區間查詢一樣的
int tquery(int a,int b)//樹剖區間查詢
#includeusing namespace std;
using namespace std;
struct nodee[200001];
int pos[100001],tid[100001],top[100001],dep[100001],son[100001],siz[100001],f[100001],head[100001];
int d[100001][5],sum[500001],col[200001],lzy[500001];
int n,m,cnt,cnt2;
void add(int u,int v)//加邊函式,記得加雙向
void dfs1(int u,int fa)//兩個dfs初始化
}void dfs2(int u,int tp)
}void pushup(int id)
void pushdown(int id)
if(col[id]) }
void build(int id,int l,int r)
void update1(int id,int l,int r,int x,int y,int z)//區間加數
int mid=(l+r)>>1;
pushdown(id);
if(x<=mid) update1(id<<1,l,mid,x,y,z);
if(y>mid) update1(id<<1|1,mid+1,r,x,y,z);
pushup(id);
}void update2(int id,int l,int r,int x,int y,int z)//區間覆蓋
int mid=(l+r)>>1;
pushdown(id);
if(x<=mid) update2(id<<1,l,mid,x,y,z);
if(y>mid) update2(id<<1|1,mid+1,r,x,y,z);
pushup(id);
}int query(int id,int l,int r,int x,int y)//區間查詢
void tupdate(int a,int b,int c,int flag)//樹剖區間修改
int tquery(int a,int b)//樹剖區間查詢
int main()//注:在這裡我用了陣列來存邊,就不用乘以二了
while(1)
else if(s[1]=='o')
else if(s[1]=='h')
else if(s[0]=='m')
}return 0;
}
P4315 月下「毛景樹」
毛毛蟲經過及時的變形,最終逃過的一劫,離開了菜媽的菜園。毛毛蟲經過千山萬水,歷盡千辛萬苦,最後來到了小小的紹興一中的校園裡。爬啊爬 爬啊爬毛毛蟲爬到了一顆小小的 毛景樹 下面,發現樹上長著他最愛吃的毛毛果 毛景樹 上有n個節點和n 1條樹枝,但節點上是沒有毛毛果的,毛毛果都是長在樹枝上的。但是這棵 ...
洛谷P4315 月下「毛景樹」
毛毛蟲經過及時的變形,最終逃過的一劫,離開了菜媽的菜園。毛毛蟲經過千山萬水,歷盡千辛萬苦,最後來到了小小的紹興一中的校園裡。爬啊爬 爬啊爬毛毛蟲爬到了一顆小小的 毛景樹 下面,發現樹上長著他最愛吃的毛毛果 毛景樹 上有n個節點和n 1條樹枝,但節點上是沒有毛毛果的,毛毛果都是長在樹枝上的。但是這棵 ...
洛谷 P4315 月下「毛景樹」
題目 又是一道維護邊權的題 對於每乙個節點,我們把他的點權設為他跟他父親之間的邊權,這樣就輕鬆地把邊權轉化為了點權 但是,這題的重點就在 同時維護區間加和區間修改 對於乙個區間 k kk,我們設 max v k maxv k maxv k 為區間最大值,add k c k add k c k add...