• [bzoj4712] 洪水 [树链剖分+线段树+dp]


    题面

    传送门

    思路

    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
    ",getval(t1));
            }
            else{
                t1=read();t2=read();
                change(t1,t2);
            }
        }
    }
    
  • 相关阅读:
    mysql报错排查总结
    java设计模式--外观模式
    java设计模式--策略模式
    java设计模式--策略模式
    java设计模式--简单工厂
    java设计模式--简单工厂
    国外有哪些比较好的IT社区
    使用jmeter进行性能测试-Jmeter教程及技巧汇总 (转)
    Fiddler环境配置教程
    Fiddler+Jmeter+断言详细教程
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/9602550.html
Copyright © 2020-2023  润新知