【题解】BZOJ3252 攻略(max 卷积)
今天学了点有关这个的有趣的东西,做一道题练练手。显然这题有好多简单的贪心方法。
给定
n 个节点的带点权的树(点权为正),选出k 条根到叶子的路径使得路径并的权值和最大。
容易想到
最后
我们发现这是一个类似卷积的东西,称此为 max 卷积。一个重要的性质是,两个上凸函数做 max 卷积,最终得到的还是上凸函数(下凸的做 min 卷积也是同理的)。同时我们称此为两个凸壳的闵科夫斯基和。
我们把转移写的奇怪一点:
我们发现
怎么求闵科夫斯基和呢?考虑维护斜率(这里即差分),对于每个
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=2e5+9;
inline long long read() {
register long long x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
int n,k,a[N];
vector<int>e[N];
int ls[N],rs[N],val[N],d[N],rt[N],id[N];
int merge(int x,int y) {
if(x==0||y==0) return x+y;
if(val[y]>val[x]) swap(x,y);
rs[x]=merge(rs[x],y);
if(d[ls[x]]<d[rs[x]]) swap(ls[x],rs[x]);
d[x]=d[rs[x]]+1;
return x;
}
int find(int x) {return rt[x]==x?x:rt[x]=find(rt[x]);}
void del(int x) {
rt[x]=rt[ls[x]]=rt[rs[x]]=merge(ls[x],rs[x]);
rs[x]=ls[x]=d[x]=0;
}
void dfs(int u,int fa) {
for(auto v:e[u]) if(v!=fa) {
dfs(v,u);
if(!id[u]) id[u]=id[v];
else id[u]=merge(id[u],id[v]);
}
if(!id[u]) {
id[u]=u, rt[u]=u, val[u]=a[u];
} else {
val[id[u]]+=a[u];
}
}
signed main() {
n=read(), k=read();
rep(i,1,n) a[i]=read();
rep(i,2,n) {
int u=read(), v=read();
e[u].push_back(v), e[v].push_back(u);
}
dfs(1,0);
int ans=0, now=rt[id[1]];
while(now&&k) {
k--;
ans+=val[now];
del(now);
now=rt[now];
}
printf("%lld\n",ans);
return 0;
}