线段树区间更新经典题目( D - Naive Operations HDU - 6315 )

In a galaxy far, far away, there are two integer sequence a and b of length n. 
b is a static permutation of 1 to n. Initially a is filled with zeroes. 
There are two kind of operations: 
1. add l r: add one for al,al+1...aral,al+1...ar 
2. query l r: query ∑ri=l⌊ai/bi⌋∑i=lr⌊ai/bi⌋

Input

There are multiple test cases, please read till the end of input file. 
For each test case, in the first line, two integers n,q, representing the length of a,b and the number of queries. 
In the second line, n integers separated by spaces, representing permutation b. 
In the following q lines, each line is either in the form 'add l r' or 'query l r', representing an operation. 
1≤n,q≤1000001≤n,q≤100000, 1≤l≤r≤n1≤l≤r≤n, there're no more than 5 test cases. 

Output

Output the answer for each 'query', each one line. 

Sample Input

5 12
1 5 2 4 3
add 1 4
query 1 4
add 2 5
query 2 5
add 3 5
query 1 5
add 2 4
query 1 4
add 2 5
query 2 5
add 2 2
query 1 5

Sample Output

1
1
2
4
4
6

题意 : 有两个长度均为n的数组  刚开始a数组全为0 , b数组人为输入   现在进行两种操作

add  l,r,    即给a数组的[l, r ] 区间内的每一个数均加上1

query  l, r   询问 ⌊ai/bi⌋的和  ( i 在[l,r] 内);

 

 

思路 :设有变量c  add  , c代表该区间内的最小b值 ,叶子节点的c即为对应的b数组的值   ,add为该区间内共加了多少次1 ,

每加一次1  ,对应区间的add + 1, c -1 , 如果c > 0 则不用向下处理 ,否则c == 0 证明 该区间内有叶子节点sum需要更新将该

节点的add向下传递,则其子节点的c 减对应add   该节点的add值为0 ,以此类推至叶子节点 ,对应叶子节点的c重新为对应的b 

 add为0 , pushdown   , 将该节点的add向下传递,则其子节点的c 减对应add   该节点的add值为0 ,以此类推 ,最后回溯 ,

更新每个节点的sum与c

AC代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
int date[100005];
struct node
{
    int add; ///该区间加了几次1
    int c ; ///该区间的最小值 每来一次1 c-- r 如果c = 0
    int l;  ///证明区间有sum值可以被更新
    int r;
    int sum;
}s[400005];
int build(  int i , int a, int b )
{
    s[i].l = a;
    s[i].r = b;
    s[i].sum = 0;
    s[i].add = 0;
    if( a == b )
    {
        s[i].c = date[a];
        return 1;
    }
    else
    {
        int mid = (a+b)/2;
        build( 2*i, a, mid );
        build( 2*i+1, mid+1, b);
        s[i].c = min( s[2*i].c, s[2*i+1].c);
    }

}
void pushdown( int i ) ///将add值推下去
{
    if( s[i].add )
    {
        s[2*i].add += s[i].add;
        s[2*i+1].add += s[i].add;
        s[2*i].c -= s[i].add;
        s[2*i+1].c -= s[i].add;
        s[i].add  = 0;
    }
}
void  add( int i, int a , int b )
{
    if( s[i].l   ==  a && s[i].r ==  b && s[i].c > 1 ) 
    {
        s[i].c--;
        s[i].add++;
        return ;      ///如果该节点区间全部包含在更新范围内 且c > 1 则直接处理  不在向下更新
    }
    else
        if( s[i].l == s[i].r && s[i].c <=1 )
    {
        s[i].sum++;          ///若为叶子节点且c<=1 更新sum c 
        s[i].add = 0;
        s[i].c = date[s[i].l];
        return ;
    }
    else
    {                        ///向下传递add
        pushdown( i );
        int mid = ( s[i].l + s[i].r )/2;
        if( b <= mid )
        {
           add( 2*i, a, b);
        }
        else
            if( a > mid )
        {
            add( 2*i +1, a , b );
        }
        else
        {
            add( 2*i, a, mid );
            add( 2*i+1, mid+1, b );
        }
        s[i].sum = s[2*i].sum + s[2*i+1].sum; ///回溯
        s[i].c = min( s[2*i].c , s[2*i+1].c );
    }
}
int query( int i, int a, int b )
{
    if( s[i].l == a && s[i].r == b )
    {
        return s[i].sum;
    }
    int mid = ( s[i].l + s[i].r )/2;
    if( b <= mid )
    {
        return query( 2*i, a, b );
    }
    else
        if( a > mid )
    {
        return query( 2*i+1, a, b );
    }
    else
    {
        return query( 2*i, a, mid )+query( 2*i+1, mid+1, b );
    }
}
int main()
{
    int n, q;
    int i , j,  k;
    char str[10];
    int a, b;
    while( scanf("%d%d", &n, &q) != EOF )
    {
        memset( date, 0, sizeof( date ));
        memset( s, 0 , sizeof( s ));
        for( i = 1; i<=n; i++)
        {
            scanf("%d", &date[i]);
        }
        build( 1, 1 , n );
        for( i = 0; i<q; i++)
        {
            scanf("%s", str );
            //printf("%s\n", str);
            scanf("%d%d", &a, &b);
            if( str[0] == 'a')
            {
                add( 1, a, b );
            }
            else
                if( str[0] == 'q')
            {
               int ans =  query( 1, a, b );
               printf("%d\n", ans);
            }
            memset( str, 0, sizeof( str ));
        }
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值