ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-08-07 19:59:04  阅读:266  来源: 互联网

标签:ch edgs 树状 int modify 线段 Tidy sum 布阵


总结:

此题主要使用的是线段树的单点修改和区间求和的操作。

或者使用树状数组的区间求和的操作

题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=1166

题目:

Problem Description

C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视。
中央情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工兵营地一共有多少人,例如Derek问:“Tidy,马上汇报第3个营地到第10个营地共有多少人!”Tidy就要马上开始计算这一段的总人数并汇报。但敌兵营地的人数经常变动,而Derek每次询问的段都不一样,所以Tidy不得不每次都一个一个营地的去数,很快就精疲力尽了,Derek对Tidy的计算速度越来越不满:"你个死肥仔,算得这么慢,我炒你鱿鱼!”Tidy想:“你自己来算算看,这可真是一项累人的工作!我恨不得你炒我鱿鱼呢!”无奈之下,Tidy只好打电话向计算机专家Windbreaker求救,Windbreaker说:“死肥仔,叫你平时做多点acm题和看多点算法书,现在尝到苦果了吧!”Tidy说:"我知错了。。。"但Windbreaker已经挂掉电话了。Tidy很苦恼,这么算他真的会崩溃的,聪明的读者,你能写个程序帮他完成这项工作吗?不过如果你的程序效率不够高的话,Tidy还是会受到Derek的责骂的.

 

Input

第一行一个整数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条命令

 

Output

对第i组数据,首先输出“Case i:”和回车,
对于每个Query询问,输出一个整数并回车,表示询问的段中的总人数,这个数保持在int以内。

 

Sample Input

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

 

Sample Output

Case 1:

6

33

59

分析1:线段树

此题可以使用线段树的方式,

节点结构为:

struct Node

{

        int l,r;

        int sum; //此为维护的答案

只需要使用到pushup,modify,build和query操作。

线段树模板题题解,使用pushup,query,modify和build操作的链接:

其中build()时会再建树的过程中先初始化值。

pushup通过子节点更新父结点的sum值。

代码实现:

# include <iostream>
# include <stdio.h>
using namespace std;

const int N = 50010;

struct Node
{
    int l,r;
    int sum;
}edgs[N * 4];

int a[N];

void pushup(int u)
{
    edgs[u].sum = edgs[2 * u].sum + edgs[2 * u + 1].sum;
}

void build(int u , int l ,int r)
{
    edgs[u].l = l,edgs[u].r = r;
    if(l == r)
    {
        edgs[u].sum = a[l];
    }
    else
    {
        int mid = (edgs[u].l + edgs[u].r) / 2;
        build(2 * u , l , mid);
        build(2 * u +1 , mid +1 , r);
        pushup(u);
    }
}

void modify(int u , int x , int v)
{
    if(edgs[u].l == x && edgs[u].r == x)
    {
        edgs[u].sum += v;
        return;
    }
    else
    {
        int mid = (edgs[u].l + edgs[u].r) / 2;
        if(x <= mid)
        {
            modify(2 * u , x , v);
        }
        else
        {
            modify(2 * u + 1 , x , v);
        }
        pushup(u);
    }
}

int query(int u , int l , int r)
{
    if(edgs[u].l >= l && edgs[u].r <= r)
    {
        return edgs[u].sum;
    }
    int total = 0;
    int mid = ( edgs[u].l + edgs[u].r ) / 2;
    if(l <= mid)
    {
        total += query(2 * u , l , r);
    }
    if(r > mid)
    {
        total += query(2 * u + 1 , l , r);
    }
    return total;
}

int main()
{
    int t;
    scanf("%d",&t);
    for(int j = 1 ; j <= t ; j++)
    {
        printf("Case %d:\n",j);
        int n;
        scanf("%d",&n);
        for(int i = 1 ; i <= n ; i++)
        {
            scanf("%d",&a[i]);
        }
        build(1,1,n);
        string ch;
        while(cin >> ch)
        {
            if(ch == "End")
            {
                break;
            }
            else if(ch == "Query")
            {
                int l,r;
                scanf("%d %d",&l,&r);
                printf("%d\n",query(1,l,r));
            }
            else if(ch == "Add")
            {
                int t,v;
                scanf("%d %d",&t,&v);
                modify(1,t,v);
            }
            else
            {
                int t,v;
                scanf("%d %d",&t,&v);
                modify(1,t,-v);
            }
        }
    }
}

分析2:树状数组

 使用树状数组维护前缀和,便于单点修改 和 区间求和。

使用模板即可。

树状数组模板题解:

 代码实现:

# include <iostream>
# include <stdio.h>
# include <string>
# include <cstring>
using namespace std;

const int N = 50010;

int t[N];

int n;

int lowbit(int x)
{
    return x & -x;
}

void modify(int idx , int v)
{
    for(int i = idx ; i <= n ; i += lowbit(i))
    {
        t[i] += v;
    }
}

int query(int idx) // 求1~idx 的值
{
    int res = 0;
    for(int i = idx ; i ; i -= lowbit(i))
    {
        res += t[i];
    }
    return res;
}

int main()
{
    int loop;
    scanf("%d",&loop);
    for(int j = 1; j <= loop ; j++)
    {
        memset(t,0,sizeof t);
        printf("Case %d:\n",j);
        scanf("%d",&n);
        for(int i = 1 ; i <= n ; i++)
        {
            int temp;
            scanf("%d",&temp);
            modify(i,temp);
        }
        string ch;
        while(cin >> ch)
        {
            if(ch == "End")
            {
                break;
            }
            else if(ch == "Query")
            {
                int l,r;
                scanf("%d %d",&l,&r);
                printf("%d\n",query(r) - query(l - 1)); // l~r的值
            }
            else if(ch == "Add")
            {
                int x , v;
                scanf("%d %d",&x,&v);
                modify(x,v);
            }
            else
            {
                int x,v;
                scanf("%d %d",&x,&v);
                modify(x,-v);
            }
        }
    }
    return 0;
}

标签:ch,edgs,树状,int,modify,线段,Tidy,sum,布阵
来源: https://blog.csdn.net/qq_49120553/article/details/119491585

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有