您的位置:首页 > 其它

BZOJ3252: 攻略

2017-08-23 13:42 375 查看

BZOJ3252: 攻略

贪心·线段树

http://blog.csdn.net/mys_c_k/article/details/66474976

题目大意:

给定一棵以1为根的n个点的树,树有点权且点权为正整数,可以选择k条以根作为起点的路径,每条路径的价值即这条路径上所有点的点权之和。

但是选择一条路径之后,这条路径上的所有点的点权会变成0。(也就是说,这k条路径中被重复选择的点,其点权只能被计算一次)。

求最大价值之和。n<=200000。

题解:

首先维护一个前缀和,每个点x的前缀和即x到根的所有点权之和。

然后很明显就要贪心的来做了,第一次肯定选取前缀和最大的那个点。

但是选了这个点之后,这个点到根的路径上的所有点的点权被“取走(也就是变成0)”了;

那么观察可知,如果把x这个点取走,辣么说它和它的子树中所有点的前缀和都要减去这个点的权值(注意不是前缀和)。

于是我们每次选取一个前缀和最大的点,从这个点开始往上走一直走到不能走为止,其间对于每个走过的点都进行上述“取走”操作。

显然我们的单次修改操作都是针对一颗子树的,所以显然想到是dfs序。

又因为我们需要维护这样一个数据结构,实现区间减法和询问整个区间的最值,那么显然就是来一发线段树就可以啦~

然后看复杂度,显然每次选择x就把x到根所有的点都取走是不划算的(因为有可能这条路径上的点已经取走了,不用再取一遍了)

于是我们的策略是记录每个点是否被删除,这样选择x的话,从x开始一直往上走,走到一个已经被取过的点就停止。

那么由于每个点只可能被取走一次,取走一次的复杂度是在线段树上进行区间操作的O(lgn),所以复杂度就是O(nlgn)。

Code:

#include <iostream>
#include <cstring>
#include <cstdio>
#define D(x) cout<<#x<<"
4000
= "<<x<<"  "
#define E cout<<endl
using namespace std;
typedef long long LL;
const LL N = 200005;
typedef pair<LL,LL> pii;

LL n,K,w
,s
,pa
,pos
,ptr
,end
,tim,ans; bool del
;

struct Edge{
LL to,next;
} e[N*2];
LL ec=0, head
;
void add(LL a,LL b){
ec++; e[ec].to=b; e[ec].next=head[a]; head[a]=ec;
}

void dfs(LL x,LL f){
s[x]=s[f]+w[x]; pa[x]=f;
pos[x]=++tim; ptr[pos[x]]=x;
for(LL i=head[x];i;i=e[i].next){
LL v=e[i].to;
if(v==f) continue;
dfs(v,x);
}
end[x]=tim;
}

struct Node{
LL l,r,tag;
pii mx;
} pool[N*4];

void update(LL x){
Node &t=pool[x];
if(t.l!=t.r){
pii tp=max(pool[x*2].mx,pool[x*2+1].mx);
tp.first+=t.tag; t.mx=tp;
}
else{ t.mx=make_pair(t.tag,t.l); }
}

void build(LL x,LL l,LL r){
Node &t=pool[x];
t.l=l; t.r=r; t.tag=0;
if(l!=r){
LL mid=(l+r)>>1;
build(x*2,l,mid);
build(x*2+1,mid+1,r);
}
else{ t.tag=s[ptr[l]]; }
update(x);
}

void change(LL x,LL ql,LL qr,LL d){
Node &t=pool[x];
if(ql<=t.l && t.r<=qr){ t.tag+=d; }
else{
LL mid=(t.l+t.r)>>1;
if(ql<=mid) change(x*2,ql,qr,d);
if(qr>mid) change(x*2+1,ql,qr,d);
}
update(x);
}

int main(){
freopen("a.in","r",stdin);
scanf("%lld%lld",&n,&K);
for(LL i=1;i<=n;i++) scanf("%lld",&w[i]);
LL a,b;
for(LL i=1;i<n;i++){
scanf("%lld%lld",&a,&b);
add(a,b); add(b,a);
}
dfs(1,0);
build(1,1,n);
while(K--){
for(LL p=ptr[pool[1].mx.second];p && !del[p];p=pa[p]){
//          D(p); E;
ans+=w[p]; del[p]=true;
change(1,pos[p],end[p],-w[p]);
}
}
printf("%lld\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: