题面

传送门

思路

DP方程

首先,这题如果没有修改操作就是sb题,dp方程如下

$dp[u]=max(v[u],max(dp[v]))$,其中$v$是$u$的儿子

我们令$g[u]=max(dp[v])$

修改?

我们发现,本题中所有的修改都是非负的

也就是说,每一次修改结束以后,$dp[u]$的值只增不减

同时,修改$u$位置的$v[u]$值,只会影响到$u$到根的这一条链上的$dp$值

我们考虑修改后,这条链上的每个点的$dp[u]$值的增量,令这个增量为$delta[u]$

那么显然当$delta[u]=0$时,$u$上面的所有$delta$都是0了

因此我们只需要考虑中间这一段$delta$不为0的部分,我们令这个部分为$[u,t]$

影响?

首先对于被修改的点$u$,如果本来就是$g[u]<v[u]$,那么$delta[u]=0$,不需要任何操作

否则,$delta[u]=min(v[u]+change,g[u])-v[u]$

然后,对于所有$(u,t]$中的点,它们的$g$会加上它们儿子那一层的点的$delta$

也就是说,后面这个过程中$v[x]$不受影响,所以$delta$不为0当且仅当$g[x]<v[x]$,并且这个过程中的$delta$是单调递减的

那么,一个单调递减的$delta$序列,能让你想到什么?

当然是二分查找啦!

二分

我们尝试二分得到第一个$delta[x]=0$的$x$

但是如果直接暴力算$delta$的话肯定就T飞了

我们需要一个优美一些的做法

我们考虑开一棵线段树来维护这个$delta$,同时线段树外面套一个树链剖分

显然我们只维护$delta$是无法维护的,我们还需要维护$g$的值,它的作用是询问的时候输出答案

发现这个$g$只需要叶子节点的值,所以我们可以非常方便的把开出来的$g$数组的非叶子部分作为$lazy$标记来使用

维护$delta$的时候,我们换一个方式表达它:维护$v[u]-g[u]$,这样既可以方便地区间修改,又可以当成查看$delta$是否在当前节点变成0的一部分信息

那么我们直接在线段树上二分,当前点为$u$

首先把$v[u]$修改,体现在线段树上,然后找到当前点到根的链上的$delta$0点,然后它下面的所有数的$g$值加上$delta[u]$,

二分的右端点,我们考虑它的$delta$,并以这个值为新的$delta$,重复上面的二分过程(注意不用修改$v[u]$)

查询的时候就是用记录好的$v[u]$和用线段树中维护的$g[u]$求最小值输出

Code

代码里面的s等价于上述的g

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cassert>
#define ll long long
using namespace std;
inline ll read(){
    ll re=0,flag=1;char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') flag=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    return re*flag;
}
int n,fa[200010],first[200010],dep[200010],siz[200010],son[200010],top[200010];
ll w[200010],s[200010],dp[200010];
int p[200010],back[200010],clk;
ll a[1000010],add[1000010];
struct edge{
    int to,next;
}e[400010];int cnte=0;
inline void adde(int u,int v){
    e[++cnte]=(edge){v,first[u]};first[u]=cnte;
    e[++cnte]=(edge){u,first[v]};first[v]=cnte;
}
void dfs(int u,int f){
    int i,v;dep[u]=dep[f]+1;siz[u]=1;fa[u]=f;dp[u]=w[u];
    for(i=first[u];~i;i=e[i].next){
        v=e[i].to;if(v==f) continue;
        dfs(v,u);
        siz[u]+=siz[v];s[u]+=dp[v];
        if(siz[son[u]]<=siz[v]) son[u]=v;
    }
    if(siz[u]==1) s[u]=1e15;
    dp[u]=min(dp[u],s[u]);
}
void dfs2(int u,int t){
    int i,v;
    top[u]=t;
    clk++;p[u]=clk;back[clk]=u;
    if(son[u]) dfs2(son[u],t);
    for(i=first[u];~i;i=e[i].next){
        v=e[i].to;if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}
void update(int num){
    a[num]=min(a[num<<1],a[num<<1|1]);
}
void push(int l,int r,int num){
    if(l==r||!add[num]) return;
    add[num<<1]+=add[num];add[num<<1|1]+=add[num];
    a[num<<1]-=add[num];a[num<<1|1]-=add[num];
    add[num]=0;
}
void build(int l,int r,int num){
    if(l==r){
        a[num]=w[back[l]]-s[back[l]];
        add[num]=s[back[l]];
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,num<<1);build(mid+1,r,num<<1|1);
    update(num);
}
ll ask1(int l,int r,int num,int pos){
    if(l==r) return add[num];
    push(l,r,num);
    int mid=(l+r)>>1;
    if(mid>=pos) return ask1(l,mid,num<<1,pos);
    else return ask1(mid+1,r,num<<1|1,pos);
}
int get(int l,int r,int ql,int qr,int num,ll delta){
    push(l,r,num);
    if(l>=ql&&r<=qr){
        if(a[num]>=delta) return l;
        if(l==r) return 0;
    }
    int mid=(l+r)>>1;
    if(qr<=mid) return get(l,mid,ql,qr,num<<1,delta);
    if(ql>mid) return get(mid+1,r,ql,qr,num<<1|1,delta);
    int tmp=get(mid+1,r,ql,qr,num<<1|1,delta);
    if(!tmp||tmp>mid+1) return tmp;
    tmp=get(l,mid,ql,qr,num<<1,delta);
    if(!tmp) return mid+1;
    else return tmp;
}
void changew(int l,int r,int num,int pos){
    if(l==r){
        a[num]=w[back[l]]-add[num];
        return;
    }
    int mid=(l+r)>>1;
    push(l,r,num);
    if(mid>pos) changew(l,mid,num<<1,pos);
    else changew(mid+1,r,num<<1|1,pos);
    update(num);
}
void changes(int l,int r,int ql,int qr,int num,ll val){
    push(l,r,num);
    if(l>=ql&&r<=qr){
        a[num]-=val;add[num]+=val;
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=ql) changes(l,mid,ql,qr,num<<1,val);
    if(mid<qr) changes(mid+1,r,ql,qr,num<<1|1,val);
    update(num);
}
int binary_search(int x,ll val){
    int pos,t;t=x;x=fa[x];
    while(fa[x]){
        pos=get(1,n,p[top[x]],p[x],1,val);
        if(!pos) break;
        if(pos>p[top[x]]) return back[pos];
        t=top[x];x=fa[t];
    }
    return t;
}
void work(int x,int y,ll val){
    while(top[x]!=top[y]) changes(1,n,p[top[x]],p[x],1,val),x=fa[top[x]];
    changes(1,n,p[y],p[x],1,val);  
}
ll getval(int x){
    return min(w[x],ask1(1,n,1,p[x]));
}
void change(int x,ll val){
    ll tmp=getval(x);
    w[x]+=val;
    changew(1,n,1,p[x]);
    ll delta=getval(x)-tmp;
    if(!delta) return;
    while(fa[x]){
        int y=binary_search(x,delta);
        if(x!=y) work(fa[x],y,delta);
        x=fa[y];
        if(!x) return;
        tmp=getval(x);
        work(x,x,delta);
        delta=getval(x)-tmp;
        if(!delta) return;
    }
}
int main(){
    memset(first,-1,sizeof(first));int i,t1;ll t2;char s[10];
    n=read();
    for(i=1;i<=n;i++) w[i]=read();
    for(i=1;i<n;i++){
        t1=read();t2=read();
        adde(t1,t2);
    }
    dfs(1,0);
    dfs2(1,1);
    build(1,n,1);
    int m=read();
    while(m--){
        scanf("%s",s);
        if(s[0]=='Q'){
            t1=read();
            printf("%lld\n",getval(t1));
        }
        else{
            t1=read();t2=read();
            change(t1,t2);
        }
    }
}

[bzoj4712] 洪水 [树链剖分+线段树+dp]的更多相关文章

  1. 【bzoj4712】洪水 树链剖分+线段树维护树形动态dp

    题目描述 给出一棵树,点有点权.多次增加某个点的点权,并在某一棵子树中询问:选出若干个节点,使得每个叶子节点到根节点的路径上至少有一个节点被选择,求选出的点的点权和的最小值. 输入 输入文件第一行包含 ...

  2. 【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp

    题目描述 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块和. 其中,一棵子树的最大连通子块和指的是:该子树 ...

  3. BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)

    前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...

  4. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  5. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  6. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  7. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  8. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  9. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  10. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

随机推荐

  1. 模拟ie9的placeholder

    ie9 的input框没有placeholder属性 啧啧啧~~~ 所以就用span标签来模拟一下 先判断浏览器类型 if(navigator.useAgent.indexOf("MSIE ...

  2. SpringMVC-实现PUT请求上传文件(转)

    因为在图片上传的时候使用的是二进制的方式上传,所以使用隐藏域进行方法转换方式失效,转方法: https://www.cnblogs.com/morethink/p/6378015.html 可是后来我 ...

  3. TA-LIB】之MACD

    移动平滑异同平均线(Moving Average Convergence Divergence,简称MACD指标)策略.MACD是查拉尔·阿佩尔(Geral Appel)于1979年提出的,由一快及一 ...

  4. 12 new方法和单例、定制访问函数、装饰器

    new方法和单例.定制访问函数.装饰器 上节课作业解答 # 通过多重继承方法,分别定义出动物,人类,和和荷兰人三种类 class Animal(object): def __init__(self, ...

  5. 第一章:Hello, World!

    感谢作者 –> 原文链接 本文翻译自The Flask Mega-Tutorial Part I: Hello, World! 一趟愉快的学习之旅即将开始,跟随它你将学会用Python和Flas ...

  6. linux c scanf()小解

    今天学习了新的内容,关于c语言的scanf()函数. scanf()函数,读取指定格式的值赋值给相应变量.空格(‘ ‘),回车('\n'),TAB是分隔符,轻易不会被读取.还有,该函数的返回值是正确读 ...

  7. HTML5 canvas 圆盘抽奖

    使用html5 canvas 绘制的圆盘抽奖程序 效果图: 贴上全部代码:  1 <!DOCTYPE html> <html> <head> <meta ch ...

  8. 斐波那契数列(递归)&求100以内的素数

    Java 5 添加了 java.util.Scanner 类,这是一个用于扫描输入文本的新的实用程序.它是以 前的 StringTokenizer 和 Matcher 类之间的某种结合.由于任何数据都 ...

  9. Postman-CI集成Jenkins(3)

    Postman-CI集成Jenkins(3) Postman-简单使用 Postman-进阶使用 Postman-CI集成Jenkins Newman 官方说明:Postman's command-l ...

  10. 2016年后web开发趋势是什么?

    2016 年后 Web开发趋势是什么 来源:yafeilee.me 发布时间:2016-05-06 阅读次数:1378 3   近二年的进展 前端发展日新月异, 甚至有一句戏言: "每六星期 ...