洛谷P4556 雨天的尾巴

2022-05-08 15:45:08 字數 1737 閱讀 1102

p4556 [vani有約會]雨天的尾巴 /【模板】線段樹合併

題目大意:

有一顆 \(n\) 個節點的樹,\(m\) 次操作,每次將節點 \(u\) 到 \(v\) 的路徑上的每個點放乙個物品 \(c\) ,最後詢問每個節點上數量最多的物品是什麼,其中數量相同的物品取編號最小者,若無物品輸出0。

\(n,m,c\leq 10^5\)

思路:

如題目名稱,模板題,蒟蒻之前沒寫過線段樹合併,就簡單總結一下吧。

線段樹合併有兩種寫法:

1.將 \(b\) 直接合併到 \(a\) 上去:

int merge(int a,int b)else

return a;

}

這種寫法會使 \(a\) 直接繼承了部分 \(b\) 的節點,所以之後對 \(a\) 進行其他的修改時,\(b\) 也會被改掉,因此這麼寫只適用與離線查詢(如此題)。

2.新建節點儲存合併後的樹

int merge(int a,int b)else

return root;

}

這樣寫就可以適用於動態查詢,其缺點是空間複雜度比較大,在離線查詢時線段樹的空間約為前者的兩倍。

對於這道題,我們考慮樹上差分,對於每乙個節點建一顆動態開點權值線段樹,然後dfs時節點和自己的所有兒子合併即可。

細節:

code:

#include#include#include#include#include#define n 100100

#define r 100000

using namespace std;

inline int read()

while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();

return s*w;

}struct treet[17*4*n];

int head[n],to[n*2],nxt[n*2];

int cnt_n,cnt_e;

int root[n];

int ancest[n],fa[n];

int ans[n];

vector> upd[n];

bool vis[n];

void init(int n)

void add_e(int a,int b,bool id)

void update(int x)

int find(int x)

int merge(int a,int b)else

return a;

}void tarjan(int x,int fath)

vis[x]=true;

for(int i=upd[x].size()-1;i>=0;i--)

}fa[x]=fath;

}void dfs(int x)

ans[x]=t[root[x]].mx>0?t[root[x]].node:0;

}int main()

for(int i=0;itarjan(1,0);

dfs(1);

for(int i=1;i<=n;i++)

printf("%d\n",ans[i]);

return 0;

}

P4556 雨天的尾巴

題目背景 深繪里一直很討厭雨天。灼熱的天氣穿透了前半個夏天,後來一場大雨和隨之而來的洪水,澆滅了一切。雖然深繪里家鄉的小村落對洪水有著頑固的抵抗力,但也倒了幾座老房子,幾棵老樹被連根拔起,以及田地裡的糧食被弄得一片狼藉。無奈的深繪里和村民們只好等待救濟糧來維生。不過救濟糧的發放方式很特別。題目描述 ...

P4556 雨天的尾巴 線段樹合併

題目背景 深繪里一直很討厭雨天。灼熱的天氣穿透了前半個夏天,後來一場大雨和隨之而來的洪水,澆滅了一切。雖然深繪里家鄉的小村落對洪水有著頑固的抵抗力,但也倒了幾座老房子,幾棵老樹被連根拔起,以及田地裡的糧食被弄得一片狼藉。無奈的深繪里和村民們只好等待救濟糧來維生。不過救濟糧的發放方式很特別。題目描述 ...

P4556 雨天的尾巴 樹上差分, 樹鏈剖分

鏈結 給出一棵n n個結點的樹,有mm 次修改操作,每次操作要求將a,b a,b之間最短路徑所有點加上型別為c c的糧食11次,到最後輸出每個點所儲存的最多的糧食型別,如果有相等數量的,則輸出型別編號最小的.n,m c 1 05n,m,c 1 05整體使用 set set 維護,會發現由於左兒子對右...