线段树专题 H - Can you answer these queries? (区间更新 开方操作)

H - Can you answer these queries?

题目链接

ps:十分有趣的一道题目。一直在想如何用lazy标记,来使区间更新不超时间。

题意:

有n个数(1 <= n <= 100000),数最大为2^63。有m次操作(1 <= m <= 100000) ,操作有一下两种:

  1. 使区间X到Y的所有数都开平方 (0)
  2. 输出区间X到Y的所有数的和 (1)

输入:

先输入n(10)
输入n(10)个数
输入m(5)
输入m(5)次操作 操作类型(0代表开方,1代表求值)以及X和Y代表区间的左右坐标

10
1 2 3 4 5 6 7 8 9 10
5
0 1 10
1 1 10
1 1 5
0 5 8
1 4 8

输出:

Case #1:
19
7
6

思路:

本题难在更新,在最开始时,对区间更新可以对区间内每一个点进行单点更新,但是复杂度极高。但是我们会发现即使是最大的2^63,在7次开方之后归1,且1开平方也是1。所以我们可以用一个标记来对树的节点进行标记。如果该节点范围内所有树都为1,则遇到开方操作可以无视。这样一来,我们就找到了优化更新的方法

代码如下:

#include <stdio.h>
#include <cmath>
#include <string.h>
#include <algorithm>
using namespace std;

const int maxn = 1e5 + 10;

long long num[maxn];

struct Tree
{
    int lson , rson ;
    long long sum;
    bool is1;
}tree[maxn * 4];


void Push_one(int h)
{
    tree[h].is1 = tree[h << 1].is1 && tree[h << 1 | 1].is1;
}

void Build (int l , int  r ,int h)
{
    tree[h].lson = l;
    tree[h].rson = r;
    if (l == r)
    {
        if (num[l] == 1)
            tree[h].is1 = true;
        else
            tree[h].is1 = false;

        tree[h].sum = num[l];
        return;
    }
    int mid = (l + r) >> 1;
    Build (l , mid , h << 1) ;
    Build (mid + 1 , r , h << 1 | 1);
    tree[h].sum = tree[h << 1].sum + tree[h << 1 | 1].sum;
    Push_one(h);
}

void Update (int l , int r , int h)
{
    if (tree[h].is1)
        return ;
    if (tree[h].lson == tree[h].rson)
    {
        tree[h].sum = sqrt(tree[h].sum);
        if (tree[h].sum == 1)
            tree[h].is1 = true;
            return ;
    }
    int mid = (tree[h].lson + tree[h].rson ) >> 1;
    if (r <= mid)
        Update (l , r , h << 1 );
    else if (l > mid)
        Update (l , r , h << 1 | 1);
    else
    {
        Update ( l , mid , h << 1);
        Update ( mid + 1 , r , h << 1 | 1);
    }
    tree[h].sum = tree[h << 1].sum + tree[h << 1 | 1].sum;
    Push_one(h);
}

long long Query (int  l , int  r ,  int h)
{
    if (l == tree[h].lson && r == tree[h].rson)
    {
        return tree[h].sum;
    }
    int mid = (tree[h].lson + tree[h].rson) >> 1;
    if (mid < l)
        Query (l , r , h << 1 | 1);
    else if(mid >= r)
        Query (l , r , h << 1);
    else
        return Query (l , mid , h << 1) + Query (mid +1  , r , h << 1 | 1);

}

int main()
{
    int n;
    int test = 1;
    while (~scanf ("%d" , &n))
    {

        memset (tree , 0 , sizeof (tree));
        for (int i = 1 ; i <= n ; i++)
            scanf ("%lld" , &num[i]);
        Build (1 , n , 1);
        int m;
        scanf ("%d" , &m);
        int a , b , c;
        printf ("Case #%d:\n" , test);
        test++;
        while (m--)
        {
            scanf ("%d%d%d" , &a , &b , &c);
            if(b > c)
                swap(b , c);
            if (a)
                printf ("%lld\n" , Query (b , c , 1));
            else
                Update (b , c , 1);
        }
        printf("\n");
    }
    return 0 ;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值