Can you answer these queries IV(线段树区间和)

You are given a sequence A of N(N <= 100,000) positive integers. There sum will be less than 1018. On this sequence you have to apply M (M <= 100,000) operations:

(A) For given x,y, for each elements between the x-th and the y-th ones (inclusively, counting from 1), modify it to its positive square root (rounded down to the nearest integer).

(B) For given x,y, query the sum of all the elements between the x-th and the y-th ones (inclusively, counting from 1) in the sequence.
Input

Multiple test cases, please proceed them one by one. Input terminates by EOF.

For each test case:

The first line contains an integer N. The following line contains N integers, representing the sequence A1..AN.
The third line contains an integer M. The next M lines contain the operations in the form "i x y".i=0 denotes the modify operation, i=1 denotes the query operation.
Output

For each test case:

Output the case number (counting from 1) in the first line of output. Then for each query, print an integer as the problem required.

Print an blank line after each test case.

See the sample output for more details.
Example

Input:
5
1 2 3 4 5
5
1 2 4
0 2 4
1 2 4
0 4 5
1 1 5
4
10 10 10 10
3
1 1 4
0 2 3
1 1 4

Output:
Case #1:
9
4
6

Case #2:
40
26
.
/*
  线段树 -- 进阶
  题目要求 求区间和 + 更新区间(对区间的元素进行开方)
  这里处理就是 利用优化 找到要一个完整的更新的区间
  (这里只能是一个完整的区间不能是一个子区间 因为 为了让update方便更新 :即一次操作一个节点的区间 所以modify只传了一个节点所在的区间)
  然后在update里面进行开方
  并进行区间sum的更新
  https://vjudge.net/contest/13207#problem/D
*/


#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>

using namespace std;

struct LNode
{
    int l;
    int r;
    long long sum;//区间和
}node[100000 * 4];

void build(int l,int r,int rt)
{
    node[rt].l = l;
    node[rt].r = r;
    if(l == r)
    {
        scanf("%lld",&node[rt].sum);
        return;
    }
    int mid = (l + r )/2;
    build(l,mid,rt*2);
    build(mid+1,r,rt*2+1);
    node[rt].sum = node[rt *2].sum + node[rt*2+1].sum;
}
void update(int l,int r,int rt)//更新区间 这里就是开平方
{
    if(l == r)//到达叶子节点
    {
        node[rt].sum = (long long ) sqrt(node[rt].sum);//开方
        return;
    }
    int mid  = (l + r) /2;
    //算是一种优化 就是如果这个区间全部都是1 那么就不进行开方了
    if(node[rt*2].sum != (mid - l +1))
        update(l,mid,rt*2);
    if(node[rt*2 + 1].sum != (r - mid))
        update(mid+1,r,rt*2 + 1);
    node[rt].sum = node[rt *2].sum + node[rt*2+1].sum;//更新当前节点的sum值
}
void modify(int l,int r,int rt)//调整区间和
{
    if(node[rt].l == l && node[rt].r == r)//找到一个完全重合的区间
    {
        if(node[rt].sum != (r - l +1))//优化 如果区间全为1 那么就不在进行更新了
            update(l,r,rt);
        return;
    }
    int mid = (node[rt].l + node[rt].r)/2;
    if(mid >= r)//完全位于左子树
    {
        modify(l,r,rt*2);
    }
    else if(mid < l)//完全位于右子树
    {
        modify(l,r,rt*2+1);
    }
    else//位于两颗子树之间
    {
        modify(l,mid,rt*2);
        modify(mid +1,r,rt*2+1);
    }
    node[rt].sum = node[rt *2].sum + node[rt*2+1].sum;
}
long long query(int l,int r,int rt)
{
    if(l <= node[rt].l && r >= node[rt].r)//找到其子区间
    {
        return node[rt].sum;
    }
    int mid = (node[rt].l + node[rt].r)/2;
    if(mid >= r)
    {
        return query(l,r,rt*2);
    }
    else if(mid < l)
    {
        return query(l,r,rt*2+1);
    }
    else
    {
        return query(l,mid,rt*2) + query(mid +1,r,rt*2+1);
    }
}
int main()
{
    int n;
    int cnt =1;
    while(cin>>n)
    {
        printf("Case #%d:\n",cnt++);
        build(1,n,1);
        int k;
        scanf("%d",&k);
        while(k--)
        {
            int op,l,r;
            scanf("%d%d%d",&op,&l,&r);
            if(l > r) swap(l,r);
            if(!op)
            {
                modify(l,r,1);
            }
            else
                printf("%lld\n",query(l,r,1));
        }
        printf("\n");
    }
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值