1. 程式人生 > >hdu 4027 Can you answer these queries?(線段樹——區間更新)(思路)

hdu 4027 Can you answer these queries?(線段樹——區間更新)(思路)

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;
}