敌兵布阵

Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u
 

Description

Lily 特别喜欢养花,但是由于她的花特别多,所以照料这些花就变得不太容易。她把她的花依次排成一行,每盆花都有一个美观值。如果Lily把某盆花照料的好的话,这盆花的美观值就会上升,如果照料的不好的话,这盆花的美观值就会下降。有时,Lily想知道某段连续的花的美观值之和是多少,但是,Lily的算术不是很好,你能快速地告诉她结果吗?

Input

	第一行一个整数T,表示有T组测试数据。
每组测试数据的第一行为一个正整数N(N<=50000),表示Lily有N盆花。接下来有N个正整数,第i个正整数ai表示第i盆花的初始美观值(1<=ai<=50)。
接下来每行有一条命令,命令有4种形式:
(1)Add i j, i和j为正整数,表示第i盆花被照料的好,美观值增加j(j<=30)
(2)Sub i j, i和j为正整数,表示第i盆花被照料的不好,美观值减少j(j<=30)
(3)Query i j, i和j为正整数,i<=j,表示询问第i盆花到第j盆花的美观值之和
(4)End,表示结束,这条命令在每组数据最后出现
每组数据的命令不超过40000条

Output

	对于第i组数据,首先输出"Case i:"和回车。
对于每个"Query i j"命令,输出第i盆花到第j盆花的美观值之和。

Sample Input

1
9
7 9 8 4 4 5 4 2 7
Query 7 9
Add 4 9
Query 3 6
Sub 9 6
Sub 3 3
Query 1 9
End

Sample Output

Case 1:
13
30
50
 

//运用线段树知识:对于查找线段树,有如下知识:

对于每一条线段A,从根节点开始遍历这棵线段树,对于每一个当前遍历的结点NODE(其实线段树中每一个结点就是一条线段),考虑四种情况:
a)如果线段A包含在线段NODE的左半区间,那么从NODE的左儿子(其实就是NODE的左半区间)开始遍历这棵树
b)如果线段A包含在线段NODE的右半区间,那么从NODE的右儿子(其实就是NODE的右半区间)开始遍历这棵树
c)如果线段A刚好和线段NODE重合,停止遍历,并将NODE中的count字段加1
d)除了以上的情况,就将线段A的左半部分在NODE的左儿子处遍历,将A的右半部分在NODE的右儿子处遍历

源代码如下:

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
struct node
{
    int left,right;
    int value;

}tree[140000];

int r[50010],sum;//r[50010]是存放每个点上的人数,sum是用来存放查询的结果。

void make (int x,int y,int num)
{
    tree[num].left=x;
    tree[num].right=y;
    if(tree[num].left==tree[num].right)
        tree[num].value=r[y];//如果x==y,说明已经是叶子节点了,没有儿子节点了,就显现成熟单个营地,人数就是r[y]
    else
        {
            make(x,(x+y)/2,num+num);//构造左儿子树
            make((x+y)/2+1,y,num+num+1);//构造右儿子树
            tree[num].value=tree[num+num+1].value+tree[num+num].value;//父节点的人数等于子结点人数之和,线段被分成两段。
        }
}

void add (int x,int y,int num)//从根节点不断往下更改,只要包含该点x的线段子都增加相应的数量y
{
    tree[num].value+=y;
    if(tree[num].left==x&&tree[num].right==x)  return;//找到x的叶子节点。停止。
    else if(x>(tree[num].left+tree[num].right)/2) add(x,y,num+num+1);//点x在该线段的右边,查询右子节点。
    else add(x,y,num+num);//否则查询左子节点
}

void sub(int x,int y,int num)
{
    tree[num].value-=y;
    if(tree[num].left==x&&tree[num].right==x)  return;
    else if(x>(tree[num].left+tree[num].right)/2) sub(x,y,num+num+1);
    else sub(x,y,num+num);
}

void query(int x,int y,int num)
{
    if(x<=tree[num].left&&y>=tree[num].right)//找到要求的线段区间,返回其值
        sum+=tree[num].value;
    else
    {
        int mid =(tree[num].left+tree[num].right)/2;
        if(x>mid) query(x,y,num+num+1);//要查询的线段在该线段右边,查询该线段的右子节点
        else if(y<=mid) query(x,y,num+num);//要查询的线段在该线段左边,查询该线段的左子节点
        else
            {
                query(x,y,num+num);//要查询的线段在该线段中间,分段查询,左右节点都查。
                query(x,y,num+num+1);
            }
    }
}

int main()
{
    int a,b,t,n;
    char command[6];
    scanf("%d",&t);
    int j=0;
    while(t--)
    {   memset(r,0,sizeof(r));
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
            scanf("%d",&r[i]);
        make(1,n,1);
        printf("Case %d:\n",++j);
        while (cin>>command)
        {
            if(strcmp(command,"End")==0)
                break;
            if(strcmp(command,"Query")==0)
            {
                scanf("%d %d",&a,&b);
                sum=0;
                query(a,b,1);
                printf("%d\n",sum);
            }
            if(strcmp(command,"Add")==0)
            {
                scanf("%d %d",&a,&b);
                add(a,b,1);
            }
            if(strcmp(command,"Sub")==0)
            {
                scanf("%d %d",&a,&b);
                sub(a,b,1);
            }
        }

    }
    return 0;
}
View Code

 

posted @ 2016-07-21 21:34  君子酱  阅读(262)  评论(0编辑  收藏  举报