HDU 1166 线段树入门

Posted ymzjj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 1166 线段树入门相关的知识,希望对你有一定的参考价值。

题目链接 HDU 1166

大概题意:

第一行一个整数T,表示有T组数据。
每组数据第一行一个正整数N(N<=50000),表示敌人有N个工兵营地,接下来有N个正整数,第i个正整数ai代表第i个工兵营地里开始时有ai个人(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条命令

 

思路:类似于区间查询和区间修改等操作,操作数又较多的情况优先想线段树、树状数组等,因为线段树又是一颗平衡二叉树,所以可以用二叉树的构建方法,在这里用的是结构数组的表示方法。

结点 :T[a, b] (a, b 表示区间 [a, b] , 其中 b-a 为长度 len )

线段树递归定义为: 

若 len > 1 , 则 [a, (a+b)/2] 为 T 的左儿子, [(a+b)/2+1, b] 为 T 的右儿子。

若 len == 1, 则 T 为叶子节点。

复杂度:

线段树的深度不超过log2len, 线段树把区间上的任意一条线段都分成不超过 2log2len 条线段。所以线段树能在O(log2len) 时间内完成一条线段的插入, 删除, 和查找等工作。

 

入门题,AC code:

///HDU 1166 线段树
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

struct
{
    int a, b, sum;  ///左端点, 右端点, 区间和
}t[140000];
int people[50010], SUM;   ///每个营地的人数

void make(int x, int y, int num)  ///x为左端点,y为右端点,num为数组下标
{
    t[num].a = x;  ///确定左端点为x
    t[num].b = y;  ///确定右端点为y

    if(x == y)     ///左端点等于右端点,说明到达叶子结点
        t[num].sum = people[y];
    else
    {
        make(x, (x+y)/2, num+num); ///递归构造左子树
        make((x+y)/2+1, y, num+num+1); ///递归构造右子树
        t[num].sum = t[num+num].sum + t[num+num+1].sum;
        ///父结点的区间和等于子树的区间和之和,因为区间被分成两半
    }
}

void add(int i, int j, int num) ///第i个堡垒加j艘船,初始nun为1,即从根结点开始
{
    t[num].sum+=j;
    if(t[num].a == i && t[num].b == i) ///找到叶子结点,返回
        return;
    if(i > (t[num].a+t[num].b)/2) ///点i在该区间的右边
        add(i, j, num+num+1);     ///递归进右结点
    else
        add(i, j, num+num);       ///否则递归进左结点
}

void sub(int i, int j, int num) ///第i个堡垒减j艘船
{
    t[num].sum-=j;
    if(t[num].a == i && t[num].b == i) ///找到叶子结点,返回
        return;
    if(i > (t[num].a+t[num].b)/2)
        sub(i, j, num+num+1);
    else
        sub(i, j, num+num);
}

void query(int i, int j, int num)    ///求i到j的总飞船长度, num初始化为1,即从根节点开始
{
    if(i <= t[num].a && j >= t[num].b)
        SUM+=t[num].sum;
    else
    {
        int mid = (t[num].a + t[num].b)/2;
        if(i > mid)
            query(i, j, num+num+1);
        else if(j <= mid)
            query(i, j, num+num);
        else
        {
            query(i, j, num+num);
            query(i, j, num+num+1);
        }
    }
}

int main()
{
    int N, T;
    char command[6];
    scanf("%d", &T);
    int j = 0;
    while(T--)
    {
        int temp, a, b;
        scanf("%d", &N);
        for(int i = 1; i <= N; i++)
        {
            scanf("%d", &people[i]);
        }
        make(1, N, 1);

        printf("Case %d:
", ++j);
        while(cin >> command)
        {
            if(strcmp(command, "End") == 0) break;
            else if(strcmp(command, "Query") == 0)
            {
                cin >> a >> b;
                SUM = 0;
                query(a, b, 1);
                cout << SUM << endl;
            }
            else if(strcmp(command, "Add") == 0)
            {
                cin >> a >> b;
                add(a, b, 1);
            }
            else if(strcmp(command, "Sub") == 0)
            {
                cin >> a >> b;
                sub(a, b, 1);
            }
        }
    }
    return 0;
}

  








以上是关于HDU 1166 线段树入门的主要内容,如果未能解决你的问题,请参考以下文章

HDU1166敌兵布阵线段树入门

HDU-1166 敌兵布阵 (线段树&&树状数组入门)

HDU 1166 敌兵布阵 线段树单点更新求和

hdu1166 敌兵布阵 树状数组/线段树

线段树入门之单点更新

HDU 1166 - 敌兵布阵 - [单点修改区间查询zkw线段树]