十二省聯考2019 春節十二響 長鏈剖分 堆

2022-05-08 03:12:09 字數 1770 閱讀 3262

[十二省聯考2019]春節十二響

可以發現每條鏈上的所有點都要放在不同的段裡,那麼最多隻需要樹的深度這麼多段就夠了。

因為這樣可以保證每條鏈上的點可以放在不同的段中而且乙個點放在這些段中一定會比新開乙個段更優。

那麼我們先考慮一條鏈的情況,顯然是先將較長的一條鏈計入答案,然後將另一條鏈上的點分別與長鏈上的點合併。

假設長鏈上的兩段分別為$a,b$,其中$a>b$,那麼對於另一條鏈上兩個點$c,d$(假設$c>d$)一定是$a$與$c$合併、$b$與$d$合併最優(即大的與大的合併,小的與小的合併)。

可以分情況討論證明一下:

假設$a>c>b>d$,$max(a,c)+max(b,d)=a+b

假設$a>c>d>b$,$max(a,c)+max(b,d)=a+d

假設$c>a>b>d$,$max(a,c)+max(b,d)=c+b

假設$c>a>d>b$,$max(a,c)+max(b,d)=c+d

假設$a>b>c>d$,$max(a,c)+max(b,d)=a+b=max(a,d)+max(b,c)=a+b$

假設$c>d>a>b$,$max(a,c)+max(b,d)=c+d=max(a,d)+max(b,c)=c+d$

那麼我們只需要將兩條鏈上點的權值都從大到小排序然後依次合併取最大值即可。

現在考慮樹的情況,可以發現對於乙個節點的所有子樹,我們依舊可以按照上述從大到小的順序將兩個子樹的最優分段方案合併。

所以只需要對於每個點維護乙個大根堆維護這個點的子樹分的所有段的權值即可。

因為每次合併的時間複雜度取決於兩個堆中較小的那個堆的大小,所以可以將原樹長鏈剖分,每個點繼承重兒子的堆然後將其他兒子的堆與重兒子的堆合併,最後將這個點的權值加入堆中即可。

因為每個點被合併一次,所以時間複雜度是$o(nlog_)$。

因為每個點繼承重兒子的堆,所以實際需要堆的數量是長鏈數。而且每個點的堆在被合併到其他堆之後就沒用了,所以可以類似記憶體**一樣**堆。

#include#include#include#include#include#include#include#include#include#include#include#define ll long long

using namespace std;

priority_queueq[200010];

int num[200010];

int son[200010];

int dep[200010];

int tot;

int head[200010];

int to[200010];

int next[200010];

int n,x;

int val[200010];

int mx[200010];

int cnt;

int sum;

ll ans;

void add(int x,int y)

void dfs(int x)

}}void solve(int x)

for(int i=head[x];i;i=next[i])

else

}for(int j=1;j<=cnt;j++)}}

if(!son[x])

ans+=val[x];

q[num[x]].push(val[x]);

}int main()

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

dfs(1);

solve(1);

printf("%lld",ans);

}

春節十二響 十二省聯考2019

給定一顆樹,要求將其上的節點分成若干組,使得每一組的節點互相不擁有祖先 後代關係。定義每一組的值為該組節點權值最大值,求值總和最小值。硬上不是很顯然的貪心,但是資料中鏈的情況給了提示。考慮鏈的情況 對於根節點兩側的鏈,我們分別排序,然後覆蓋選取即可。這個貪心的正確性是顯然的。現在考慮完整的資料 對於...

十二省聯考2019 春節十二響

點這裡看題目。感覺自己好蠢 假如我們有兩棵樹 t 1,t 2 我們應該怎麼計算出它們合併之後的最優解呢?設最優情況下,t 1 的所有記憶體段的集合為 m 1 t 2 的集合為 m 2 我們可以知道,m 1,m 2 中所有的元素都是不能再合併的 廢話 考慮有 m 1,m 2 in m 1,m 1 m ...

P5290 十二省聯考2019 春節十二響

傳送門 考慮乙個子樹裡是怎麼劃分的,維護劃分出來的每個集合的最大值,這個可以用乙個 multiset 維護 設 s x 表示節點 x 的子樹中,最優劃分 劃分出來的每個塊的節點最大值 首先葉子節點的集合顯然只有它本身 然後考慮子樹之間的合併,設兩個子樹根節點為 x,y 因為兩個子樹之間一定不會有祖先...