NYOJ 123士兵杀敌(四)【树状数组||区间树】

问题描述:区间增加V,单点查询

士兵杀敌(四)

时间限制: 2000 ms  |  内存限制: 65535 KB
难度: 5
描述

南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个人身上,这样,有时候,计算他们中的哪一个人到底有多少军功就是一个比较困难的事情,军师小工的任务就是在南将军询问他某个人的军功的时候,快速的报出此人的军功,请你编写一个程序来帮助小工吧。

假设起始时所有人的军功都是0.

输入
只有一组测试数据。
每一行是两个整数T和M表示共有T条指令,M个士兵。(1<=T,M<=1000000)
随后的T行,每行是一个指令。
指令分为两种:
一种形如
ADD 100 500 55 表示,第100个人到第500个人请战,最终每人平均获得了55军功,每次每人获得的军功数不会超过100,不会低于-100。
第二种形如:
QUERY 300 表示南将军在询问第300个人的军功是多少。
输出
对于每次查询输出此人的军功,每个查询的输出占一行。
样例输入
4 10
ADD 1 3 10
QUERY 3
ADD 2 6 50
QUERY 3
样例输出
10
60
来源
[张云聪]原创
上传者

张云聪


用线段树做TLE 了,

树状数组,用了1744MS;

Ac后学习大牛区间树的代码,用了结构体标记左右端点, 每一次加数据,相当于是在相应的区间上面加,询问的时候,将x通过的每一个区间的数据相加即可。
代码如下

树状数组:
#include<stdio.h>
int c[1000010],t,n;
int lowbit(int x) //求最低位1的位置所表示的数
{
    return x&-x;
}
void update(int p,int q)// 这个节点管辖的区间为2^k个元素。
{                       //(其中k为x二进制末尾0的个数)
    while(p<=n)
    {
        c[p]+=q;
        p+=lowbit(p);
    }
}
int s(int x)
{
    int sum=0;
    while(x>0)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}
int main()
{
    char a[10];
    int x,y,z;
    scanf("%d%d",&t,&n);
    while(t--)
    {
        scanf("%s",a);
        if(a[0]=='A')
        {
            scanf("%d%d%d",&x,&y,&z);
            update(x,z);
            update(y+1,-z);//只有这里变一下,然后就可以修改区间了
        }
        else
        {
            scanf("%d",&x);
            printf("%d\n",s(x));//s(x)表示第i个数值
                               //因为上面那个改动这里的意思变得不一样
        }
    }
}

区间树:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=1000010;
struct node
{
    int left,right,num;
} a[maxn<<2];//树的节点数是士兵人数的4倍
void build(int root,int l,int r)//初始化
{
    a[root].left=l;
    a[root].right=r;
    a[root].num=0;
    if(l<r)
    {
        int m=(l+r)>>1;
        build(root<<1,l,m);
        build(root<<1|1,m+1,r);
    }
}
void  Insert(int root,int l,int r,int v)
{
    if(l<=a[root].left&&a[root].right<=r)//这里用大于和小于号是为了应对插入范围超出士兵范围
    {
        a[root].num+=v;//a[root].num表示a[root].left到a[root].right之间的每一个点都要加的值
        return ;
    }
    int m=(a[root].left+a[root].right)>>1;
    if(r<=m)//查找范围在左边
        Insert(root<<1,l,r,v);
    else if(l>m)//查找范围在右边
        Insert(root<<1|1,l,r,v);
    else//两边都有
    {
        Insert(root<<1,l,m,v);
        Insert(root<<1|1,m+1,r,v);
    }
}
int query(int root,int x)
{
    if(a[root].left==a[root].right)
        return a[root].num;
    int sum=a[root].num;//x经过的每一个点都要加,因为x经过root,表示x包含在a[root].left到a[root].right内
    int m=(a[root].left+a[root].right)>>1;
    if(x<=m)
        sum+=query(root<<1,x);
    else
        sum+=query(root<<1|1,x);
    return sum;
}
int main()
{
    int m,n,x,y,z,i;
    char c[10];
    while(~scanf("%d%d",&m,&n))
    {
        build(1,1,n);
        for(i=0; i<m; i++)
        {
            scanf("%s",c);
            if(c[0]=='A')
            {
                scanf("%d%d%d",&x,&y,&z);
                Insert(1,x,y,z);
            }
            else if(c[0]=='Q')
            {
                scanf("%d",&x);
                printf("%d\n",query(1,x));
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值