HDU 6315 - Naive Operations [2018杭电多校联赛第二场 G](线段树区间更新)

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

【题意】
给定两个长度为n的整数序列a和b,a初始值为全0,b的初始值为一个[1,n]的排列,现在有以下两种操作:
1.add L R 把序列a中[L,R]的元素全部加1
2.query L R 求区间[L,R]的所有ai/bi的和,ai/bi是整除

【输入格式】
多组输入,第一行两个整数n,q(n,q<=1e5)代表序列长度和操作次数,以下q行每行一种操作,分别为一个字符串和两个整数,即题意中的格式.

【输出格式】
对每次query操作,输出对应的查询结果

【思路】
线段树区间更新,线段树维护两个值,一个是b的最小值minb,另一个是最终答案ans,对于每个线段树的叶子结点赋初值为b[i],每次add操作时让b[i]-1,因为是整除,所以当b[i]减到0的时候最终的答案才会更新。所以在区间更新的时候,只有递归到该区间的最小值大于1或者该区间是单点的时候才停止,区间最小值大于1可以直接打lazy标记,单点可以直接将minb减1同时判断是否为0,如果该区间不是单点且最小值又刚好是1那么只能递归更新左右子结点的值。

#include<bits/stdc++.h>
using namespace std;
#define node tree[id]
#define lson tree[id<<1]
#define rson tree[id<<1|1]

const int maxn=100050;

int n,q;
int b[maxn];
struct Tree{
    int left,right;
    int minb,ans;
    int lazy;
}tree[maxn<<2];

void pushup(int id){
    node.minb=min(lson.minb,rson.minb);
    node.ans=lson.ans+rson.ans;
}

void pushdown(int id){
    if(node.lazy && node.left!=node.right){
        lson.lazy+=node.lazy;
        rson.lazy+=node.lazy;
        lson.minb-=node.lazy;
        rson.minb-=node.lazy;
        node.lazy=0;
    }
}

void build(int id,int le,int ri){
    node.left=le;
    node.right=ri;
    node.lazy=0;
    if(le==ri){
        node.minb=b[le];
        node.ans=0;
        return;
    }
    int mid=(le+ri)>>1;
    build(id<<1,le,mid);
    build(id<<1|1,mid+1,ri);
    pushup(id);
}

void add(int id,int le,int ri){
    if(node.left==le && node.right==ri && node.minb>1){
        node.lazy+=1;
        node.minb-=1;
        return;
    }
    if(node.left==node.right){
        node.minb-=1;
        if(node.minb==0){
            node.minb=b[node.left];
            ++node.ans;
        }
        return;
    }
    pushdown(id);
    int mid=(node.left+node.right)>>1;
    if(ri<=mid) add(id<<1,le,ri);
    else if(le>mid) add(id<<1|1,le,ri);
    else{
        add(id<<1,le,mid);
        add(id<<1|1,mid+1,ri);
    }
    pushup(id);
}

int query(int id,int le,int ri){
    if(node.left==le && node.right==ri){
        return node.ans;
    }
    pushdown(id);
    int mid=(node.left+node.right)>>1;
    if(ri<=mid) return query(id<<1,le,ri);
    else if(le>mid) return query(id<<1|1,le,ri);
    else return query(id<<1,le,mid)+query(id<<1|1,mid+1,ri);
}

int main(){
    while(scanf("%d%d",&n,&q)==2){
        for(int i=1;i<=n;++i) scanf("%d",&b[i]);
        build(1,1,n);
        while(q--){
            char op[15];
            int x,y;
            scanf("%s%d%d",op,&x,&y);
            if(op[0]=='a') add(1,x,y);
            else printf("%d\n",query(1,x,y));
        }
    }
    return 0;
}

还有另一种思路比较好想,线段树中只记录最小值和最小值的下标(如果这段区间有多个相同最小值只记录最左端的最小值下标),最终的答案存到一个BIT里,每次区间减1,直接打lazy标记就可以,然后查询对应的区间最小值是否为0,为0则根据维护的下标值把这段区间里的所有0都恢复成b[i],同时更新BIT里的最终答案

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
#define node tree[id]
#define lson tree[id<<1]
#define rson tree[id<<1|1]
const int maxn=100050;

int n,q;
int b[maxn];
int bit[maxn];
struct Tree{
    int left,right;
    int minb,pos;
    int lazy;
}tree[maxn<<2];

void add(int i,int x){
    while(i<=n){
        bit[i]+=x;
        i+=i&-i;
    }
}

int sum(int i){
    int s=0;
    while(i>0){
        s+=bit[i];
        i-=i&-i;
    }
    return s;
}

void pushup(int id){
    if(lson.minb<=rson.minb){
        node.minb=lson.minb;
        node.pos=lson.pos;
    }
    else{
        node.minb=rson.minb;
        node.pos=rson.pos;
    }
}

void pushdown(int id){
    if(node.lazy && node.left!=node.right){
        lson.lazy+=node.lazy;
        rson.lazy+=node.lazy;
        lson.minb-=node.lazy;
        rson.minb-=node.lazy;
        node.lazy=0;
    }
}

void build(int id,int le,int ri){
    node.left=le;
    node.right=ri;
    node.lazy=0;
    if(le==ri){
        node.minb=b[le];
        node.pos=le;
        return;
    }
    int mid=(le+ri)>>1;
    build(id<<1,le,mid);
    build(id<<1|1,mid+1,ri);
    pushup(id);
}

void update(int id,int le,int ri,int val){
    if(node.left==le && node.right==ri){
        node.lazy+=val;
        node.minb-=val;
        return;
    }
    pushdown(id);
    int mid=(node.left+node.right)>>1;
    if(ri<=mid) update(id<<1,le,ri,val);
    else if(le>mid) update(id<<1|1,le,ri,val);
    else{
        update(id<<1,le,mid,val);
        update(id<<1|1,mid+1,ri,val);
    }
    pushup(id);
}

pii query(int id,int le,int ri){
    if(node.left==le && node.right==ri){
        return pii(node.minb,node.pos);
    }
    pushdown(id);
    int mid=(node.left+node.right)>>1;
    if(ri<=mid) return query(id<<1,le,ri);
    else if(le>mid) return query(id<<1|1,le,ri);
    else{
        pii a=query(id<<1,le,mid);
        pii b=query(id<<1|1,mid+1,ri);
        if(a.first<=b.first) return a;
        else return b;
    }
}

int main(){
    while(scanf("%d%d",&n,&q)==2){
        memset(bit,0,sizeof(bit));
        for(int i=1;i<=n;++i) scanf("%d",&b[i]);
        build(1,1,n);
        while(q--){
            char op[15];
            int x,y;
            scanf("%s%d%d",op,&x,&y);
            if(op[0]=='a'){
                update(1,x,y,1);
                int le=x,ri=y;
                while(le<=ri){
                    pii tmp=query(1,le,ri);
                    int pos=tmp.second;
                    if(tmp.first==0){
                        update(1,pos,pos,-b[pos]);
                        add(pos,1);
                        le=pos+1;
                    }
                    else break;
                }
            }
            else{
                int ans=sum(y)-sum(x-1);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xiao_k666/article/details/81235097