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

来源:互联网 发布:平面设计作图软件 编辑:程序博客网 时间:2024/05/24 02:47

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 100010typedef __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,ritypedef __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;}
1 0