BZOJ3252: 攻略
Description
题目简述:树版[k取方格数]
众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏。
今天他得到了一款新游戏《XX半岛》,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景。
所有场景和选择支构成树状结构:开始游戏时在根节点(共通线),叶子节点为结局。
每个场景有一个价值,现在桂马开启攻略之神模式,同时攻略k次该游戏,问他观赏到的场景的价值和最大是多少
(同一场景观看多次是不能重复得到价值的)
“为什么你还没玩就知道每个场景的价值呢?”
“我已经看到结局了。”
Input
第一行两个正整数n,k
第二行n个正整数,表示每个场景的价值
以下n-1行,每行2个整数a,b,表示a场景有个选择支通向b场景(即a是b的父亲)
保证场景1为根节点
n<=200000,1<=场景价值<=2^31-1
Output
输出一个整数表示答案
Sample Input
5 2
4 3 2 1 1
1 2
1 5
2 3
2 4
4 3 2 1 1
1 2
1 5
2 3
2 4
Sample Output
10
题解Here!
树的形态没有改变,套上树剖。
问最大价值和,贪心地在吗每个叶子结点中选取树上前缀和最大的节点,计入答案,并删去这个叶子结点到根的所有贡献。
但是每次到根复杂度经不起了,所以开个$bool$数组记录当前节点是否被删除了贡献,若已删除,则跳出。
至于区间最值与最值位置,懒得写$ST$表了,直接线段树搞搞事。。。
线段树真$NB$。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #define LSON rt<<1 #define RSON rt<<1|1 #define DATA(x) b[x].data #define POS(x) b[x].pos #define SIGN(x) b[x].c #define LSIDE(x) b[x].l #define RSIDE(x) b[x].r #define WIDTH(x) (RSIDE(x)-LSIDE(x)+1) #define MAXN 200010 using namespace std; int n,m,c=1,d=1; int val[MAXN],head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],pos[MAXN],top[MAXN]; long long sum[MAXN]; bool used[MAXN]; struct Tree{ int next,to; }a[MAXN<<1]; struct Segment_Tree{ long long data,pos,c; int l,r; }b[MAXN<<2]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } inline void add(int x,int y){ a[c].to=y;a[c].next=head[x];head[x]=c++; a[c].to=x;a[c].next=head[y];head[y]=c++; } void dfs1(int rt){ son[rt]=0;size[rt]=1; for(int i=head[rt];i;i=a[i].next){ int will=a[i].to; if(!deep[will]){ deep[will]=deep[rt]+1; sum[will]=sum[rt]+val[will]; fa[will]=rt; dfs1(will); size[rt]+=size[will]; if(size[son[rt]]<size[will])son[rt]=will; } } } void dfs2(int rt,int f){ id[rt]=d++;pos[id[rt]]=rt;top[rt]=f; if(son[rt])dfs2(son[rt],f); for(int i=head[rt];i;i=a[i].next){ int will=a[i].to; if(will!=fa[rt]&&will!=son[rt])dfs2(will,will); } } inline void pushup(int rt){ DATA(rt)=max(DATA(LSON),DATA(RSON)); if(DATA(LSON)>DATA(RSON))POS(rt)=POS(LSON); else POS(rt)=POS(RSON); } inline void pushdown(int rt){ if(!SIGN(rt)||LSIDE(rt)==RSIDE(rt))return; SIGN(LSON)+=SIGN(rt);DATA(LSON)+=SIGN(rt); SIGN(RSON)+=SIGN(rt);DATA(RSON)+=SIGN(rt); SIGN(rt)=0; } void buildtree(int l,int r,int rt){ LSIDE(rt)=l;RSIDE(rt)=r;SIGN(rt)=0; if(l==r){ DATA(rt)=sum[pos[l]]; POS(rt)=l; return; } int mid=l+r>>1; buildtree(l,mid,LSON); buildtree(mid+1,r,RSON); pushup(rt); } void update(int l,int r,long long c,int rt){ if(l<=LSIDE(rt)&&RSIDE(rt)<=r){ SIGN(rt)+=c;DATA(rt)+=c; return; } pushdown(rt); int mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)update(l,r,c,LSON); if(mid<r)update(l,r,c,RSON); pushup(rt); } long long query(int l,int r,int rt){ long long ans=0; if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt); pushdown(rt); int mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)ans=max(ans,query(l,r,LSON)); if(mid<r)ans=max(ans,query(l,r,RSON)); return ans; } void work(){ long long ans=0; for(int i=1;i<=m;i++){ ans+=query(1,n,1); int x=pos[POS(1)]; while(x&&used[x]){ update(id[x],id[x]+size[x]-1,-val[x],1); used[x]=false; x=fa[x]; } } printf("%lld\n",ans); } void init(){ int x,y; n=read();m=read(); for(int i=1;i<=n;i++){ val[i]=read(); used[i]=true; } for(int i=1;i<n;i++){ x=read();y=read(); add(x,y); } deep[1]=1;sum[1]=val[1]; dfs1(1); dfs2(1,1); buildtree(1,n,1); } int main(){ init(); work(); return 0; }