2015 Multi-University Training Contest 2 - Gorgeous Sequence - (线段树,区间更新,区间查询)

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=5306

题意:给出数字A,有3种操作,0操作将区间[x,y]中的每个元素ai替换为min(ai,t),1操作查询区间[x,y]中的最大值,2操作查询区间中[x,y]的元素和。

解析:此题用到一种比较复杂的懒惰标记方法。主要一点是每个区间统计有多少个元素不大于t;这样统计sum时分两步,先统计这些不大于t的元素的和,再将大于t的元素用t替换算入sum中;统计max类似。具体见代码。

代码

#include <bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long ll;
const ll maxN=1e6+5;
const ll MaxVal=1ll<<31;
const ll MinVal=0-MaxVal;

int n,m;
struct node{
    ll sum;
    int blowT,maxV,tag;
    //blowT记录该区间有多少个数不大于t,maxV记录区间的最大值
    //tag是lazy标识记录区间需要进行的更新的状态,sum记录区间元素之和
}tree[maxN<<2];
void pushup(int rt)//用子区间的更新结果来更新父区间
{
    tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
    tree[rt].maxV=max(tree[rt<<1].maxV,tree[rt<<1|1].maxV);
    tree[rt].blowT=tree[rt<<1].blowT+tree[rt<<1|1].blowT;
}

void build(int l,int r,int rt)
{
    if(l==r)
    {
        scanf("%d",&tree[rt].tag);
        tree[rt].sum=tree[rt].maxV=tree[rt].tag;
        tree[rt].blowT=1;
        return;
    }
    tree[rt].tag=0;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt);
}
void modify(int l,int r,int rt,int t)//根据t值修改区间[l,r]的元素值
{
    if(tree[rt].tag&&tree[rt].tag<=t) return;
    tree[rt].tag=t;//tag==0是由于calc中的设定
    
    if(tree[rt].blowT!=r-l+1)
    {
        //(r-l+1-tree[rt].blowT)是区间中比t大的元素个数
        //比t大的要置成t,sum在calc函数中处理后其实代表的是那些不大于t的元素之和
        tree[rt].maxV=t;
        tree[rt].sum+=(ll)t*(r-l+1-tree[rt].blowT);
        tree[rt].blowT=r-l+1;
    }
}
void pushdown(int l,int r,int rt)//父区间tag信息传递给子区间
{
    if(tree[rt].tag)
    {
        int m=(l+r)>>1;
        modify(lson,tree[rt].tag);
        modify(rson,tree[rt].tag);
        tree[rt].tag = 0;
    }
}
void calc(int l,int r,int rt,int t)//在每次修改的时候,清理子树中所有大于等于t的标记。计算元素初步值
{
    if(tree[rt].maxV<t) return;        //不需要更新
    if(tree[rt].tag>=t) tree[rt].tag=0;//本来需要更新为tree[rt].tag,它大于t时就不用了
    if(l==r)
    {
        tree[rt].sum=tree[rt].maxV=tree[rt].tag;//此时如果tree[rt].tag=0,即元素值大于t是不计入的,它需要在后面的modify函数中计算
        tree[rt].blowT=tree[rt].tag?1:0;//记录当前不大于t的元素的个数
        return;
    }
    pushdown(l,r,rt);
    int m=(l+r)>>1;
    calc(lson,t);
    calc(rson,t);
    pushup(rt);
}
void update(int L,int R,int t,int l,int r,int rt)
{
    if(tree[rt].maxV<=t) return;
    if(L<=l&&r<=R)//如果当前区间[l,r]被要更新区间[L,R]所包含
    {
        if(l==r)//因为不大于t前面会return,所以此处一定是大于t的,所以需要强行修改为t
        {
            tree[rt].sum=tree[rt].tag=tree[rt].maxV=t;
            tree[rt].blowT=1;
        }else { //先行遍历到底,更新tag
            calc(l,r,rt,t);
        }
        modify(l,r,rt,t);
    }
    else
    {
        pushdown(l,r,rt);
        int m=(l+r)>>1;
        if(L<=m) update(L,R,t,lson);
        if(R>m) update(L,R,t,rson);
        pushup(rt);
    }
}
int queryMax(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R) return tree[rt].maxV;
    int m=(l+r)>>1;
    int maxtemp=MaxVal;
    pushdown(l,r,rt);
    if(L<=m) maxtemp=max(maxtemp,queryMax(L,R,lson));
    if(R>m) maxtemp= max(maxtemp,queryMax(L,R,rson));
    pushup(rt);
    return maxtemp;
}
ll querySum(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R) return tree[rt].sum;
    int m=(l+r)>>1;
    ll sum=0;
    pushdown(l,r,rt);
    if(L<=m) sum+=querySum(L,R,lson);
    if(R>m) sum+=querySum(L,R,rson);
    pushup(rt);
    return sum;
}
int main()
{
    int T,x,y,op,t;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        build(1,n,1);
        while(m--)
        {
            scanf("%d%d%d",&op,&x,&y);
            if(x>y) swap(x,y);
            if(op==0)
            {
                scanf("%d",&t);
                update(x,y,t,1,n,1);
            }else if(op==1){
                printf("%d\n",queryMax(x,y,1,n,1));
            }else{
                printf("%lld\n",querySum(x,y,1,n,1));
            }
        }
    }
    return 0;
}

参考http://www.shuizilong.com/house/archives/hdu-5306-gorgeous-sequence/

           http://www.cnblogs.com/crackpotisback/p/4677511.html

           https://www.cnblogs.com/shenben/p/6641984.html

           https://blog.csdn.net/crzbulabula/article/details/54139413

猜你喜欢

转载自blog.csdn.net/sdau20163942/article/details/81099501