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); }
相关文章推荐
- BZOJ3252 攻略
- BZOJ3252: 攻略 可并堆
- 【贪心】 BZOJ 3252:攻略
- BZOJ_3252_攻略_线段树+dfs序
- [bzoj3252]攻略
- [bzoj3252][NSOI2017]攻略
- 【贪心】 BZOJ 3252:攻略
- 【BZOJ3252】攻略
- BZOJ3252 攻略(贪心+dfs序+线段树)
- BZOJ3252: 攻略
- BZOJ 3252攻略 dfs序+线段树
- 【bzoj3252】攻略
- dfs序+线段树 BZOJ3252 攻略
- BZOJ 3252: 攻略 贪心 树链剖分
- bzoj 3252: 攻略
- 【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心
- BZOJ3252 攻略
- Dfs【bzoj3252】攻略
- BZOJ3252: 攻略
- BZOJ3252 攻略 [树链剖分][不用线段树]