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 維護,會發現由於左兒子對右...