【Bzoj3252】攻略(dfs序+线段树)

Description

题目链接

Solution

可以想到,每次肯定是拿最大价值为最优

考虑改变树上一个点的值,只会影响它的子树,也就是dfs序上的一个区间,

于是可以以dfs序建线段树,这样就变成区间问题了

Code

#include <cstdio>
#include <algorithm>
#define MID int mid=(l+r)>>1,ls=id<<1,rs=id<<1|1
#define ll long long
#define N 200010
using namespace std;

struct xds{ll x,tag;}T[N<<2];
struct info{int to,nex;}e[N<<1];
int n,k,tot,head[N],val[N],dfn[N],bel[N],fa[N],R[N],x;
ll Ans,sum[N];
bool vis[N];

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void Link(int u,int v){
    e[++tot].nex=head[u];e[tot].to=v;head[u]=tot;
}

void dfs(int u){
    bel[dfn[u]=++tot]=u;sum[tot]=sum[dfn[fa[u]]]+val[u];
    for(int i=head[u];i;i=e[i].nex){
        int v=e[i].to;
        if(v==fa[u]) continue;
        fa[v]=u;
        dfs(v);
    }
    R[u]=tot;
}

void build(int l,int r,int id){
    if(l==r){T[id].x=sum[l];return;}
    MID;
    build(l,mid,ls);
    build(mid+1,r,rs);
    T[id].x=max(T[ls].x,T[rs].x);
}

void pushdown(int id){
    ll &tag=T[id].tag;
    if(!tag) return;
    int ls=id<<1,rs=id<<1|1;
    T[ls].x+=tag;T[ls].tag+=tag;
    T[rs].x+=tag;T[rs].tag+=tag;
    tag=0;
}

void Find(int l,int r,int id){
    if(l==r){x=bel[l];return;}
    pushdown(id);
    MID;
    if(T[ls].x>T[rs].x) Find(l,mid,ls);
    else Find(mid+1,r,rs);
    T[id].x=max(T[ls].x,T[rs].x);
}

void Modify(int l,int r,int id,int ql,int qr,int x){
    if(l>=ql&&qr>=r){
        T[id].x+=x;T[id].tag+=x;return;
    }
    pushdown(id);
    MID;
    if(ql<=mid) Modify(l,mid,ls,ql,qr,x);
    if(qr>mid) Modify(mid+1,r,rs,ql,qr,x);
    T[id].x=max(T[ls].x,T[rs].x);
}

int main(){
    n=read(),k=read();
    for(int i=1;i<=n;++i) val[i]=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        Link(u,v);Link(v,u);
    }
    tot=0;dfs(1);
    build(1,n,1);
    while(k--)
    {
        Ans+=T[1].x;
        Find(1,n,1);
        for(;x&&!vis[x];vis[x]=1,x=fa[x]) Modify(1,n,1,dfn[x],R[x],-val[x]);
    }
    printf("%lld\n",Ans);
    return 0;
}

原文地址:https://www.cnblogs.com/void-f/p/8676378.html

时间: 2024-08-04 17:23:22

【Bzoj3252】攻略(dfs序+线段树)的相关文章

【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心

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

POJ 3321 DFS序+线段树

单点修改树中某个节点,查询子树的性质.DFS序 子树序列一定在父节点的DFS序列之内,所以可以用线段树维护. 1: /* 2: DFS序 +线段树 3: */ 4:   5: #include <cstdio> 6: #include <cstring> 7: #include <cctype> 8: #include <algorithm> 9: #include <vector> 10: #include <iostream> 1

codevs1228 (dfs序+线段树)

总结: 第一次遇到dfs序的问题,对于一颗树,记录节点 i 开始搜索的序号 Left[i] 和结束搜索的序号 Righti[i],那么序号在 Left[i] ~ Right[i] 之间的都是节点 i 子树上的节点. 并且此序号与线段树中 L~R 区间对应,在纸上模拟了几遍确实如此,但暂时还未理解为何对应. 此题就是dfs序+线段树的裸题 代码: #include<iostream> #include<vector> #include<cstring> #include&

[BZOJ 3306]树(dfs序+线段树+倍增)

Description 给定一棵大小为 n 的有根点权树,支持以下操作: • 换根 • 修改点权 • 查询子树最小值 Solution 单点修改子树查询的话可以想到用dfs序+线段树来处理,换根的处理画一画图应该可以明白: 如果查询的x是当前的根rt,直接返回整棵树的min 如果rt在x的子树中,用倍增的方法找到离x最近的rt的祖先t,整棵树除t的子树以外的部分就是x当前根下的子树 如果rt不在x的子树中,查询x原来的子树的min值 #include<iostream> #include<

Educational Codeforces Round 6 E dfs序+线段树

题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili上电子科技大学发的视频学习的 将一颗树通过dfs编号的方式 使每个点的子树的编号连在一起作为相连的区间 就可以配合线段树搞子树 因为以前好像听说过 线段树可以解决一种区间修改和查询区间中不同的xx个数...所以一下子就想到了... 但是我不会写线段树..只会最简单的单点修改区间查询...不会用延迟标

【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树

题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\),问你答案是多少. \(n\leq {10}^5,k\leq {10}^9\) 题解 设\(l\)为这棵树的叶子个数,显然当\(k>\)树的深度时答案都是\(l\). 下面要证明:答案是\(O(l+\frac{n-l}{k})\)的. 我们从下往上贪心,每次选择一个未被覆盖的深度最深的点,覆盖这个点网

codeforces 343D Water Tree 树链剖分 dfs序 线段树 set

题目链接 这道题主要是要考虑到同一棵子树中dfs序是连续的 然后我就直接上树剖了... 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=600005; 4 5 struct Node 6 { 7 int l,r; 8 int value; 9 void init() 10 { 11 l=r=value=0; 12 } 13 }tree[4*MAXN]; 14 vector<int>nei[MAXN]

Manthan, Codefest 16(G. Yash And Trees(dfs序+线段树))

题目链接:点击打开链接 题意:给你一棵树, 根结点为1, q组操作, 每组操作有两种, 一种是对一个结点的所有子树结点的值全部+1, 另一种是查询一个结点的子树结点上值%m的余数为素数的个数. 思路:对于第一个操作, 我们可以想到用dfs序给树重新标号, 使得一个结点的子树结点为相邻的一条线段, 这样,就可以很容易的用线段树进行处理了.  对于第二个操作, 为了维护一个区间内的值, 我们可以用bitset作为结点信息.  我们可以开一个m位的bitset, 对于每个位, 1表示这个数在此区间中,

cdoj 574 High-level ancients dfs序+线段树

High-level ancients Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/574 Description Love8909 is keen on the history of Kingdom ACM. He admires the heroic undertakings of Lxhgww and Haibo. Inspired by those sagas, L