P10641 BZOJ3252 攻略 题解
题目传送门
Solution
长链剖分大佬们都将的很清楚了,我就来补一发左偏树吧。
我们可以每个节点建一个大根堆,然后自底向上合并。
因为每个节点对答案的贡献最多只有一次,我们可以 dfs 一遍,把
其实就是将每一层的最大元素连成一条链。
最后的左偏树中取前
比如这棵树(样例),
我们先将最底层的
然后将
再将
最后将
取前
其中
Code(100 pts):
#include<bits/stdc++.h>
#define afor(x,y,z) for(int x=y;x<=z;x++)
#define bfor(x,y,z) for(int x=y;x>=z;x--)
#define cfor(x,y) for(int x=head[y];x;x=nxt[x])
using namespace std;
typedef long long ll;
typedef const int cint;
cint N=2e5+10;
ll n,k,rt,ans,v[N],w[N],d[N],lson[N],rson[N];
ll tot,head[N],to[N],nxt[N];
void add(ll x,ll y) {
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
ll merge(ll x,ll y) {
if(!x||!y) return x+y;
if(w[x]<w[y]) swap(x,y);
rson[x]=merge(rson[x],y);
if(d[lson[x]]<d[rson[x]])
swap(lson[x],rson[x]);
d[x]=d[rson[x]]+1;
return x;
}
ll dfs(ll x) {
ll ret=0;
cfor(i,x) {
ll y=dfs(to[i]);
if(y) ret=merge(ret,y);
else ret=merge(ret,to[i]);
}
if(ret) w[ret]+=w[x];
return ret;
}
int main() {
scanf("%lld%lld",&n,&k);
afor(i,1,n) scanf("%lld",&w[i]);
afor(i,1,n-1) {
ll x,y;
scanf("%lld%lld",&x,&y);
add(x,y),v[y]=1;
}
afor(i,1,n) if(!v[i]) rt=i;
ll heap=dfs(rt);
afor(i,1,k) {
ans+=w[heap];
heap=merge(lson[heap],rson[heap]);
}
printf("%lld",ans);
return 0;
}