[bzoj3252]攻略【dfs序】【线段树】
【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=3252
【题解】
一个显而易见的贪心:每次一定取价值和最大的路径。
所以我们可以把每个点的权值设为它的到根的路径的价值和,然后按dfs序排好序后存入线段树种中。修改时,从叶节点开始往上走,每次把子树的所有点的权值减去它的价值(dfs中的一段区间)。直到遇到一个已经被修改过的点。
时间复杂度:
/* --------------
user Vanisher
problem bzoj-3252
----------------*/
# include <bits/stdc++.h>
# define ll long long
# define inf 0x3f3f3f3f
# define N 200010
using namespace std;
ll read(){
ll tmp=0, fh=1; char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
return tmp*fh;
}
struct Edge{
ll data,next;
}e[N*2];
struct Tree{
ll pl,pr,mx,tag,mxi,l,r;
}T[N*3];
ll num[N],sum[N],p[N],dad[N],head[N],place,id,l[N],r[N],now[N],n,k;
void build(ll u, ll v){
e[++place].data=v; e[place].next=head[u]; head[u]=place;
}
void dfs(ll x, ll fa){
p[x]=++id; l[p[x]]=id; dad[p[x]]=p[fa];
now[p[x]]=num[x];
sum[p[x]]=now[p[x]]+sum[p[fa]];
for (ll ed=head[x]; ed!=0; ed=e[ed].next)
if (e[ed].data!=fa) dfs(e[ed].data,x);
r[p[x]]=id;
}
void pushtag(ll x){
if (T[x].tag!=0){
if (T[x].l!=T[x].r){
T[T[x].pl].mx+=T[x].tag, T[T[x].pr].mx+=T[x].tag;
T[T[x].pl].tag+=T[x].tag, T[T[x].pr].tag+=T[x].tag;
}
T[x].tag=0;
}
}
void reget(ll x){
T[x].mx=max(T[T[x].pl].mx,T[T[x].pr].mx);
if (T[T[x].pl].mx==T[x].mx)
T[x].mxi=T[T[x].pl].mxi;
else T[x].mxi=T[T[x].pr].mxi;
}
ll create(ll l, ll r){
ll p=++place;
T[p].l=l, T[p].r=r;
if (l==r) T[p].mx=sum[l], T[p].mxi=l;
else {
ll mid=(l+r)/2;
T[p].pl=create(l,mid);
T[p].pr=create(mid+1,r);
reget(p);
}
return p;
}
void modify(ll p, ll l, ll r, ll data){
pushtag(p);
if (T[p].l==l&&T[p].r==r){
T[p].tag+=data; T[p].mx+=data;
return;
}
ll mid=(T[p].l+T[p].r)/2;
if (mid>=r) modify(T[p].pl,l,r,data);
else if (mid<l) modify(T[p].pr,l,r,data);
else modify(T[p].pl,l,mid,data), modify(T[p].pr,mid+1,r,data);
reget(p);
}
int main(){
n=read(), k=read();
for (ll i=1; i<=n; i++)
num[i]=read();
for (ll i=1; i<n; i++){
ll u=read(), v=read();
build(u,v); build(v,u);
}
place=0; dfs(1,0);
ll rt=create(1,n),ans=0;
for (ll i=1; i<=k; i++){
ll tmp=T[rt].mxi;
ans=ans+T[rt].mx;
while (now[tmp]!=0){
modify(rt,l[tmp],r[tmp],-now[tmp]);
now[tmp]=0;
tmp=dad[tmp];
}
}
printf("%lld\n",ans);
return 0;
}