BZOJ 3252 攻略 线段树

题意:链接

方法:线段树

解析:

闲的随机的题。

看完题后看着好像挺简单的。

既然每个点的权值只会传子树,并且整个图是严格的一棵树,所以应该是跟dfs序有关。

然后去看数据范围。

尼玛HINT是什么鬼。

既然这么说了那就想想怎么做吧=-=

并且因为价值都为正的,所以显然要考虑贪心,挑k条从叶节点到根的所有点权值和最大的k条。

并且每一挑完后都需要更新。

然后有一个性质,每个点至多选一次,也就是说每个点至多被删一次。

并且根节点到叶节点链上的所有点的路径上的点权和是随着深度递增的。

所以显然我们用线段树维护一个最大值以及哪个节点取到最大值就好了。

如果当前挑出来的最大值为0了,并且还没有选够k条,直接跳出就好。

然后将这些最大值累加即可。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 200010
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef long long ll;
ll n,k,tot;
ll a[N];
ll head[N],cnt;
ll fa[N],st[N],ed[N],vis[N],adfn[N];
struct node
{
    ll from,to,next;
}edge[N];
void init()
{
    memset(head,-1,sizeof(head)),cnt=1;
}
void edgeadd(ll from,ll to)
{
    edge[cnt].from=from,edge[cnt].to=to,edge[cnt].next=head[from];
    head[from]=cnt++;
}
void dfs(ll now,ll ff)
{
    st[now]=++tot,fa[now]=ff,adfn[tot]=now;
    for(ll i=head[now];i!=-1;i=edge[i].next)
    {
        ll to=edge[i].to;
        dfs(to,now);
    }
    ed[now]=tot;
}
ll ma[N<<2],col[N<<2],no[N<<2];
void pushup(ll rt)
{
    if(ma[rt<<1]>ma[rt<<1|1])
    {
        ma[rt]=ma[rt<<1];
        no[rt]=no[rt<<1];
    }else
    {
        ma[rt]=ma[rt<<1|1];
        no[rt]=no[rt<<1|1];
    }
}
void pushdown(ll rt)
{
    if(col[rt]!=0)
    {
        ma[rt<<1]+=col[rt];
        ma[rt<<1|1]+=col[rt];
        col[rt<<1]+=col[rt];
        col[rt<<1|1]+=col[rt];
        col[rt]=0;
    }
}
void build(ll l,ll r,ll rt)
{
    if(l==r)
    {
        no[rt]=l;
        return;
    }
    ll mid=(l+r)>>1;
    build(lson);
    build(rson);

}
void update(ll L,ll R,ll l,ll r,ll rt,ll v)
{
    if(L<=l&&r<=R)
    {
        ma[rt]+=v;
        col[rt]+=v;
        return;
    }
    pushdown(rt);
    ll mid=(l+r)>>1;
    if(L<=mid)update(L,R,lson,v);
    if(R>mid)update(L,R,rson,v);
    pushup(rt);
}
int main()
{
    init();
    scanf("%lld%lld",&n,&k);
    for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(ll i=1;i<n;i++)
    {
        ll x,y;
        scanf("%lld%lld",&x,&y);
        edgeadd(x,y);
    }
    dfs(1,0);
    build(1,n,1);
    for(ll i=1;i<=n;i++)
    {
        update(st[i],ed[i],1,n,1,a[i]);
    }
    ll ans=0;
    while(k--)
    {
        if(ma[1]==0)break;
        ll u=adfn[no[1]];
        ans+=ma[1];
        while(u!=0&&!vis[u])
        {
            update(st[u],ed[u],1,n,1,-a[u]);
            vis[u]=1,u=fa[u];
        }
    }
    printf("%lld\n",ans);
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-06 03:18:12

BZOJ 3252 攻略 线段树的相关文章

【贪心】 BZOJ 3252:攻略

3252: 攻略 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 261  Solved: 90[Submit][Status][Discuss] Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏. 今天他得到了一款新游戏<XX半岛>,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景.所有场景和选择支构成树状结构:开始游戏时在根节点(共通线),

bzoj 3252 攻略

题目大意: 树上每个点有一个正的点权,选k条不相交的链使权值和最大 思路: 被长链剖分的标签骗进来 法1: 这道题长链剖分标签的由来,类似重链剖分把树剖成若干条链,然后贪心的选k条即可 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<algorithm> 7 #inc

bzoj 2120: 数颜色 线段树套平衡树

/************************************************************** Problem: 2120 User: wangyucheng Language: C++ Result: Time_Limit_Exceed ****************************************************************/ #include<iostream> #include<cstdio> #incl

Bzoj 2752 高速公路 (期望,线段树)

Bzoj 2752 高速公路 (期望,线段树) 题目链接 这道题显然求边,因为题目是一条链,所以直接采用把边编上号.看成序列即可 \(1\)与\(2\)号点的边连得是. 编号为\(1\)的点.查询的时候把\(r - 1\)就好了. 这里的期望显然就是路径的平均值. 期望值: \[\dfrac{\sum_{i=l}^r\sum_{j=l}^{r}dis[i][j]}{C_{r-l+1}^2}\] 下面部分可以直接算出: 上面这一部分比较难维护. 考虑每一条边会被走过多少次. \[ans = \su

BZOJ 2124等差子序列 线段树&amp;&amp;hash

[题目描述 Description] 给一个 1 到 N 的排列{Ai},询问是否存在 1<=p1<p2<p3<p4<p5<…<pLen<=N(Len>=3),使得 Ap1,Ap2,Ap3,…ApLen 是一个等差序列. [输入描述 Input Description] 输入的第一行包含一个整数 T,表示组数. 下接 T 组数据,每组第一行一个整数 N,每组第二行为一个 1 到 N 的排列, 数字两两之间用空格隔开. [输出描述 Output Desc

[HYSBZ - 3252] 攻略

问题描述 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏<XX 半岛>,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景.所有场景和选择支构成树状 结构:开始游戏时在根节点(共通线),叶子节点为结局.每个场景有一个价值,现在桂马开启攻略之神模式,同 时攻略k次该游戏,问他观赏到的场景的价值和最大是多少(同一场景观看多次是不能重复得到价值的) "为什么你还没玩就知道每个场景的价值呢

bzoj 2962 序列操作(线段树)

题外话 做这道题我整个人都非常的绝望,推了一会发现是线段树裸题,然后调了N久一直是WA 情况是这样的 开始WA的几百毫秒的都是由于我比较SB造成的,可是跑了10几秒的程序我查了N久也查不出错 最后灵机一动把50000改成60000就过了,也不知道为啥T_T Description 一个长度为n的序列,有3种操作 1:区间加c 2:区间取为相反数 3:询问区间中选择c个数相乘的所有方案的和mod19940417的值 Solution 这个操作3非常鬼畜,似乎没啥好的办法,但是仔细推导一番会发现这个

bzoj 1835 基站选址(线段树优化Dp)

Description 题意:有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di 需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci 如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了 如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi 现在的问题是,选择基站的位置,使得总费用最小. Solution 首先可以想到dp,用dp[i][j]表示前i个村庄建了j个通讯站且第j个建在i处 dp[i][j]=min(dp[k][

bzoj 1858 序列操作(线段树)

题外话 本来想练练线段树的,然后发现这题及其蛋疼,要打一坨标记,这是我写过的最长的线段树了= = 然后我很SB的把R打成了r调了一个下午真是蛋疼QvQ Description: 给定一个0/1序列,有如下5个操作: 0:区间赋值为0 1:区间赋值为1 2:区间取反 3:询问区间内1的个数 4:询问区间内最大连续1的个数 Solution 没有操作4这显然就是个SB题,有了操作4我们需要打几个标记 我们需要从左端点开始连续0/1的个数,从右端点开始的连续0/1个数 区间内0/1个数,区间内最大连续