[BZOJ3064]Tyvj 1518 CPU监控(线段树+可持久化)

题目

传送门
这里写图片描述

题解

据ddd说非常的恶心,于是找来做,最后之后抄题解才能调对
线段树需要维护许多值:当前最大值,当前加法标记,当前覆盖标记
历史最大值,历史最大的加法标记,历史覆盖标记。
pushdown函数比较长,其他的还好搞。
- 历史最大值:考虑这个区间的历史加法和历史覆盖,以及子树的当前最大值
- 历史加法:考虑这个区间的历史加法和子树的当前加法
- 历史覆盖:考虑这个区间的当前覆盖和子树的历史覆盖
值得注意的是如果子树当前没有覆盖,那么可以直接更新加法,否则应该把加法加到覆盖上
每到一层区间就直接下放标记,无论区间是否完全覆盖

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=100001;
const int inf=2*1e9;
int T,m,cnt,add[maxn<<2],a[maxn];
char opt;
struct Node{
    int mx,add,cover;
}tree[maxn*20],his[maxn*20];//?

void pushup(int rt)
{
    tree[rt].mx=max(tree[rt<<1].mx,tree[rt<<1|1].mx);
    his[rt].mx=max(his[rt<<1].mx,his[rt<<1|1].mx);
}

void pushdown(int rt)
{
    for (int i=0; i<=1; i++)
    {
        int son=rt<<1|i;//首先更新历史 tree[rt].cover写成 tree[son].cover
        his[son].mx=max(his[son].mx,max(his[rt].cover,tree[son].mx+his[rt].add));//都是从父亲节点向下传标记 
        if (tree[son].cover!=-inf) his[son].cover=max(his[son].cover,tree[son].cover+his[rt].add);
        else his[son].add=max(his[son].add,tree[son].add+his[rt].add);
        if (tree[rt].add)//更新当前的值 
        {
            if (tree[son].cover!=-inf) tree[son].cover+=tree[rt].add;
            else tree[son].add+=tree[rt].add;
            tree[son].mx+=tree[rt].add;
        }
        if (tree[rt].cover!=-inf)//注意-inf不要写成inf 
        {
            tree[son].cover=tree[son].mx=tree[rt].cover;
            tree[son].add=0;//覆盖之后当前的add要变为零 
        }
        his[son].cover=max(his[son].cover,max(tree[son].cover,his[rt].cover));
        his[son].add=max(his[son].add,tree[son].add);
    }
    tree[rt].add=his[rt].add=0;//当前的更新后重新考虑可否计入历史 
    tree[rt].cover=his[rt].cover=-inf;
}

void build_tree(int rt,int l,int r)
{
    tree[rt].add=his[rt].add=0;
    tree[rt].cover=his[rt].cover=-inf;
    if (l==r)
    {
        tree[rt].mx=a[l];
        his[rt].mx=a[l]; return;
    }
    int mid=(l+r)>>1;
    build_tree(rt<<1,l,mid);
    build_tree(rt<<1|1,mid+1,r);
    pushup(rt);
}

void change(int rt,int L,int R,int C,int l,int r,bool ifhis)
{
    if (l!=r) pushdown(rt);//每次都要pushdown
    int mid=(l+r)>>1;
    if (L<=l && r<=R)
    {
        if (!ifhis)
        {
            tree[rt].mx+=C; tree[rt].add+=C; his[rt].add+=C; return;
        }
        else tree[rt].mx=tree[rt].cover=his[rt].cover=C;
        his[rt].mx=max(his[rt].mx,tree[rt].mx);
        return;
    }
    if (L<=mid) change(rt<<1,L,R,C,l,mid,ifhis);
    if (R>mid) change(rt<<1|1,L,R,C,mid+1,r,ifhis);
    pushup(rt);
}

int ques(int rt,int L,int R,int l,int r,bool ifhis)
{
    if (l!=r) pushdown(rt);
    int ans=-inf;
    if (L<=l && r<=R) return (ifhis?his[rt].mx:tree[rt].mx);
    int mid=(l+r)>>1;
    if (L<=mid) ans=max(ans,ques(rt<<1,L,R,l,mid,ifhis));
    if (R>mid) ans=max(ans,ques(rt<<1|1,L,R,mid+1,r,ifhis));
    return ans;
}

int main()
{
    scanf("%d",&T);
    for (int i=1; i<=T; i++) scanf("%d",&a[i]); scanf("%d",&m);
    int n=T;
    build_tree(1,1,n);
//   for (int i=1; i<=n*2; i++) printf("%d:  %d\n",i,tree[i].mx); return 0;
    for (int i=1; i<=m; i++)
    {
        int x,y,z; opt=getchar(); 
        while (opt!='Q'&&opt!='A'&&opt!='P'&&opt!='C') opt=getchar();
        if (opt=='Q') 
        {
            scanf("%d%d",&x,&y);
            printf("%d\n",ques(1,x,y,1,n,0));
        }
        if (opt=='A') 
        {
            scanf("%d%d",&x,&y);
            printf("%d\n",ques(1,x,y,1,n,1));
        }
        if (opt=='P') 
        {
            scanf("%d%d%d",&x,&y,&z);
            change(1,x,y,z,1,n,0);
        }
        if (opt=='C') 
        {
            scanf("%d%d%d",&x,&y,&z);
            change(1,x,y,z,1,n,1);
        }
    }
    return 0;
}

总结

看着zyf2000学姐的代码”肉眼调试”才改对,各种0和1,son和father,历史和现在都会搞混。迫切的提升代码能力
写代码要准,不然省选一轮GG。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值