BZOJ_3252_攻略_线段树+dfs序

Posted fcwww

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ_3252_攻略_线段树+dfs序相关的知识,希望对你有一定的参考价值。

BZOJ_3252_攻略_线段树+dfs序

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

Sample Output

10

k取方格数有一个经典的网络流做法。
由于网络流是基于贪心来找最长的一条路来增广的的,所以放到树上就转化为了k次查询根到叶子路径点权和最大的叶子编号。
然后把路径上点的贡献减去。
由于每个点最多删一次,可以每次暴力向上走,一次修改一条链的贡献。
可以把叶子的dfs序搞出来,每次修改就对应一段区间修改。
线段树维护区间最值即可。
 
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 200050
#define ls p<<1
#define rs p<<1|1
typedef long long ll;
int mx[N<<2];
ll t[N<<2],inc[N<<2],ans;
int head[N],to[N<<1],nxt[N<<1],val[N],cnt,kill[N],dfn[N],son[N],k,tot,fa[N],turn[N],n;
ll dis[N],dd[N];
inline void add(int u,int v) {
    to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
}
void dfs(int x,int y) {
    dis[x]=dis[y]+val[x];
    int i,flg=0; fa[x]=y;
    dfn[x]=tot+1;
    for(i=head[x];i;i=nxt[i]) {
        if(to[i]!=y) flg=1;
    }
    if(!flg) {
        dfn[x]=son[x]=++tot;
        turn[tot]=x;
        return ;
    }
    dfn[x]=tot+1;
    for(i=head[x];i;i=nxt[i]) {
        if(to[i]!=y) {
            dfs(to[i],x);
        }
    }
    son[x]=tot;
}
void pushup(int p) {
    if(t[ls]>t[rs]) mx[p]=mx[ls],t[p]=t[ls];
    else mx[p]=mx[rs],t[p]=t[rs];
}
void build(int l,int r,int p) {
    if(l==r) {
        mx[p]=turn[l];
        t[p]=dis[turn[l]];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,ls); build(mid+1,r,rs);
    pushup(p);
}
void pushdown(int p) {
    ll d=inc[p];
    if(d) {
        t[ls]+=d; t[rs]+=d;
        inc[ls]+=d; inc[rs]+=d;
        inc[p]=0;
    }
}
void update(int l,int r,int x,int y,int v,int p) {
    if(x<=l&y>=r) {
        t[p]+=v; inc[p]+=v; return ;
    }
    pushdown(p);
    int mid=(l+r)>>1;
    if(x<=mid) update(l,mid,x,y,v,ls);
    if(y>mid) update(mid+1,r,x,y,v,rs);
    pushup(p);
}
void solve(int x) {
    if(kill[x]||!x) return ;
    kill[x]=1;
    update(1,tot,dfn[x],son[x],-val[x],1);
    solve(fa[x]);
}
int main() {
    scanf("%d%d",&n,&k);
    int i,x,y;
    for(i=1;i<=n;i++) {
        scanf("%d",&val[i]);
    }
    for(i=1;i<n;i++) {
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    dfs(1,0);
    build(1,tot,1);
    for(i=1;i<=k;i++) {
        ans+=t[1];
        solve(mx[1]);
    }
    printf("%lld\n",ans);
}

 

以上是关于BZOJ_3252_攻略_线段树+dfs序的主要内容,如果未能解决你的问题,请参考以下文章

Bzoj3252攻略(dfs序+线段树)

BZOJ3252: 攻略 可并堆

[bzoj5379]Tree_dfs序_线段树_倍增lca

BZOJ3252: 攻略

[bzoj4345][POI2016]Korale_堆_贪心_线段树_dfs

线段树 BZOJ3252 攻略