hdu 4027 Can you answer these queries?(线段树——区间更新)(思路)

68 篇文章 0 订阅
23 篇文章 0 订阅

Can you answer these queries?

Problem Description
A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the battleships. Each of the battleships can be marked a value of endurance. For every attack of our secret weapon, it could decrease the endurance of a consecutive part of battleships by make their endurance to the square root of it original value of endurance. During the series of attack of our secret weapon, the commander wants to evaluate the effect of the weapon, so he asks you for help.
You are asked to answer the queries that the sum of the endurance of a consecutive part of the battleship line.

Notice that the square root operation should be rounded down to integer.

Input
The input contains several test cases, terminated by EOF.
For each test case, the first line contains a single integer N, denoting there are N battleships of evil in a line. (1 <= N <= 100000)
The second line contains N integers Ei, indicating the endurance value of each battleship from the beginning of the line to the end. You can assume that the sum of all endurance value is less than 263.
The next line contains an integer M, denoting the number of actions and queries. (1 <= M <= 100000)
For the following M lines, each line contains three integers T, X and Y. The T=0 denoting the action of the secret weapon, which will decrease the endurance value of the battleships between the X-th and Y-th battleship, inclusive. The T=1 denoting the query of the commander which ask for the sum of the endurance value of the battleship between X-th and Y-th, inclusive.

Output
For each test case, print the case number at the first line. Then print one line for each query. And remember follow a blank line after each test case.

Sample Input
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

Sample Output
Case #1:
19
7
6

Source
The 36th ACM/ICPC Asia Regional Shanghai Site —— Online Contest

思路:很容易想到区间更新的lazy思想,但是这道题的更新并不是简单的加减乘除,而是开方。但是开方的话我们无法通过延迟更新来减少时间复杂度,也就是说lazy思想在这里并不是太试用,那我们该怎么办呢?

考虑到数据范围为2的63次方,也就是说这个数最多只能开7次平方,因为接下来如何再开方都是1了。

所以我们就可以从这点入手,当更新时我们不延迟更新了。我们判断一下,如果这个区间可以更新,那就直接更新这个区间。如果在这个区间内tree[rt].val = ri-le+1(就是区间内所有点都为1)很明显这个时候不用更新区间,那就直接return

接下来就是简单的区间求和了

代码:

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

#define maxn 100010
typedef __int64 LL;
struct node
{
    LL val,le,ri,len;
    LL mid()
    {
        return (le+ri)>>1;
    }
} tree[maxn*4];
LL add[maxn*4],a[maxn];

void Build(LL rt,LL le,LL ri)
{
    tree[rt].le=le;
    tree[rt].ri=ri;
    tree[rt].len=ri-le+1;
    if(le==ri)
    {
        tree[rt].val=a[le];
        return ;
    }
    LL mid=tree[rt].mid();
    Build(rt<<1,le,mid);
    Build(rt<<1|1,mid+1,ri);
    tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
}

void Data(LL rt,LL left,LL right)//区间更新
{
    if(tree[rt].ri==tree[rt].le)
    {
        tree[rt].val=(LL)sqrt(1.0*tree[rt].val);
        return ;
    }
    LL mid=tree[rt].mid();
    if(right<=mid)
        Data(rt<<1,left,right);
    else if(left>mid)
        Data(rt<<1|1,left,right);
    else
    {
        Data(rt<<1,left,mid);
        Data(rt<<1|1,mid+1,right);
    }
    tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
}

void Updata(LL rt,LL left,LL right)
{
    if(tree[rt].le==left&&tree[rt].ri==right)
    {
        if(tree[rt].val==tree[rt].len)//判断是否需要进行区间更新
            return ;
        Data(rt,left,right);
        return ;
    }
    LL mid=tree[rt].mid();
    if(right<=mid)
        Updata(rt<<1,left,right);
    else if(left>mid)
        Updata(rt<<1|1,left,right);
    else
    {
        Updata(rt<<1,left,mid);
        Updata(rt<<1|1,mid+1,right);
    }
    tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
}

LL Query(LL rt,LL left,LL right)
{
    if(tree[rt].le==left&&tree[rt].ri==right)
        return tree[rt].val;
    LL mid=tree[rt].mid();
    if(right<=mid)
        return Query(rt<<1,left,right);
    else if(left>mid)
        return Query(rt<<1|1,left,right);
    else
        return Query(rt<<1,left,mid)+Query(rt<<1|1,mid+1,right);
}

int main()
{
    LL n,m,k=0;
    while(~scanf("%I64d",&n))
    {
        printf("Case #%I64d:\n",++k);
        memset(add,0,sizeof(add));
        for(LL i=1; i<=n; ++i)
            scanf("%I64d",&a[i]);
        Build(1,1,n);
        scanf("%I64d",&m);
        LL op,left,right;
        for(LL i=1; i<=m; ++i)
        {
            scanf("%I64d%I64d%I64d",&op,&left,&right);
            if(left>right)
                swap(left,right);
            if(op)
                printf("%I64d\n",Query(1,left,right));
            else
                Updata(1,left,right);
        }
        printf("\n");
    }
    return 0;
}



总结:
一方面是做题时被lazy思想固化了,老是想通过延迟更新来减少时间复杂度。。
二是没有对开方进行深入分析,只是确定它不能像加减那样延迟更新,也就是做题的侧重点没有找到。。

也学到了好多,首先lazy这个地方是多变的,就是说比如本题数据小可以不lazy。
然后是更新时更新并记录一个区间内的目标值这一步是不可省略的(也就是 tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val是必须的),因为这就是线段树的精髓了嘛


更于2017.08.07

第二次写

代码:

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

#define ll rt<<1
#define rr rt<<1|1
#define lson ll,le,mid
#define rson rr,mid+1,ri

typedef __int64 LL;
const LL maxn=1e6+10;

struct Segtree
{
    LL val,lazy,le,ri;
    LL mid()
    {
        return (le+ri)>>1;
    }
} tree[maxn<<2];
LL n,m;

void Pushup(LL rt)
{
    tree[rt].val=tree[ll].val+tree[rr].val;
    if(tree[rt].val==tree[rt].ri-tree[rt].le+1)
        tree[rt].lazy=-1;
}

void Build(LL rt,LL le,LL ri)
{
    tree[rt].le=le,tree[rt].ri=ri,tree[rt].val=0,tree[rt].lazy=0;
    if(le==ri)
    {
        scanf("%I64d",&tree[rt].val);
        if(tree[rt].val==1)
            tree[rt].lazy=-1;
        return ;
    }
    LL mid=tree[rt].mid();
    Build(lson);
    Build(rson);
    Pushup(rt);
}

void s_sqrt(LL &x,LL num)
{
    while(num--)
        x=(LL)sqrt(x*1.0);
}

void Pushdown(LL rt)
{
    if(tree[ll].lazy!=-1)
        tree[ll].lazy+=tree[rt].lazy;
    if(tree[rr].lazy!=-1)
        tree[rr].lazy+=tree[rt].lazy;
    tree[rt].lazy=0;
}

void Update(LL rt,LL le,LL ri,LL val)
{
    if(tree[rt].lazy==-1)
        return ;
    if(le<=tree[rt].le&&tree[rt].ri<=ri)
    {
        tree[rt].lazy+=val;
        return ;
    }
    if(tree[rt].lazy!=0)
        Pushdown(rt);
    LL mid=tree[rt].mid();
    if(le<=mid)
        Update(ll,le,ri,val);
    if(ri>mid)
        Update(rr,le,ri,val);
}

LL Query(LL rt,LL le,LL ri)
{
    if(le<=tree[rt].le&&tree[rt].ri<=ri)
    {
        if(tree[rt].lazy==-1)
            return tree[rt].val;
    }
    if(tree[rt].le==tree[rt].ri)
    {
        if(tree[rt].lazy>7)
        {
            tree[rt].lazy=-1;
            tree[rt].val=1;
            return tree[rt].val;
        }
        s_sqrt(tree[rt].val,tree[rt].lazy);
        if(tree[rt].val!=1)
            tree[rt].lazy=0;
        else
            tree[rt].lazy=-1;
        return tree[rt].val;
    }
    if(tree[rt].lazy!=0)
        Pushdown(rt);
    LL mid=tree[rt].mid(),ans=0;
    if(le<=mid)
        ans+=Query(ll,le,ri);
    if(ri>mid)
        ans+=Query(rr,le,ri);
    Pushup(rt);
    return ans;
}

int main()
{
    LL k=0;
//    freopen("in.txt","r",stdin);
//    freopen("myout.txt","w",stdout);
    while(~scanf("%I64d",&n))
    {
        Build(1,1,n);
        scanf("%I64d",&m);
        printf("Case #%I64d:\n",++k);
        LL op,le,ri;
        while(m--)
        {
            scanf("%I64d%I64d%I64d",&op,&le,&ri);
            if(le>ri)
                swap(le,ri);
            if(!op)
                Update(1,le,ri,1LL);
            else
                printf("%I64d\n",Query(1,le,ri));
        }
        puts("");
    }
    return 0;
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值