題解 P4315 月下「毛景樹」

2022-05-20 03:19:39 字數 3197 閱讀 8365

看原題戳這兒

如題,肯定是樹鏈剖分的題。

建議先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...