哈理工OJ 2256 南西群岛海域·冲之岛近海(线段树区间更新+前缀和优化)

题目链接:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=2256

南西群岛海域·冲之岛近海
Time Limit: 3000 MS Memory Limit: 32768 K
Total Submit: 150(58 users) Total Accepted: 56(51 users) Rating: Special Judge: No
Description
报告称本海域有敌人反攻的前兆。编成以巡洋舰为中心的警戒部队,探明敌舰队动向。

假定当前敌舰采用了排成一行的阵型,m只敌舰分别在从1到m的位置上。(包括1和m。)在一场战斗的炮击阶段,每个舰娘都可以对一个范围内的所有敌舰造成一定的伤害。如果敌舰受到的伤害大于了自己的血量,就会被击沉。可是愚蠢、调皮又二次元的提督小林,他想知道任意一个区间有多少只敌舰被击沉了。你能帮助他么?

Input
输入由有多组数据组成,每组数据的第一行包括了三个整数m、n和q,分别表示敌舰的个数、舰娘的个数和小林的询问的个数。(1≤m,n≤10^5,q≤1000)
接下来的一行包括m个整数,表示每个敌舰的血量a。(1≤a≤10^9)
接下来的n行,每行包括了三个整数,L、R、x,表示对[L,R]范围内的所有敌舰造成x点伤害。(1≤L,R≤m, 1≤x≤100)
接下来的q行,每行包括了两个整数,L和R。你需要输出[L,R]范围内被击沉的敌舰的个数。
Output
对于每个查询输出包括一行,有一个整数,表示被击沉的敌舰的个数。
Sample Input
2 1 1

1 1

1 2 2

1 2

3 1 1

1 2 3

1 3 2

1 3
Sample Output
2

1

Source
“尚学堂杯”2015级程序设计竞赛(10月)正式赛
Author
TwIStOy

【思路分析】直接区间更新,然后前缀和搞一下就OK了,可能解题方法有点挫…。
【AC代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long

struct node
{
    ll left,right,val,lazy,len;
} c[100005];

void build_tree(ll l,ll r,ll root)
{
    c[root].left=l;
    c[root].right=r;
    c[root].lazy=0;
    c[root].len=(c[root].right-c[root].left+1);
    if(c[root].left==c[root].right)
    {
        scanf("%lld",&c[root].val);
        return ;
    }
    ll mid=(c[root].left+c[root].right)/2;
    build_tree(l,mid,root*2);
    build_tree(mid+1,r,root*2+1);
    c[root].val=c[root*2].val+c[root*2+1].val;
}

void pushdown(ll root)
{
    if(c[root].lazy!=0)
    {
        c[root*2].lazy+=c[root].lazy;
        c[root*2+1].lazy+=c[root].lazy;

        c[root*2].val += c[root].lazy * (c[root*2].right - c[root*2].left + 1);
        c[root*2 + 1].val += c[root].lazy * (c[root*2 + 1].right - c[root*2 + 1].left + 1);
        c[root].lazy=0;
    }
}

ll search_tree(ll l,ll r,ll root)
{
    if(c[root].left==l&&c[root].right==r)
    {
        return c[root].val;
    }
    if(c[root].lazy!=0)
    {
        pushdown(root);
    }
    ll sum;
    ll mid=(c[root].left+c[root].right)/2;
    if(mid<l)
    {
        sum=search_tree(l,r,root*2+1);
    }
    else  if(r<=mid)
    {
        sum=search_tree(l,r,root*2);
    }
    else
    {
        sum=search_tree(l,mid,root*2)+search_tree(mid+1,r,root*2+1);
    }
    return sum;
}

void update_tree(ll l,ll r,ll root,ll x)
{
    if(c[root].left==l&&c[root].right==r)
    {
        c[root].lazy+=x;
        c[root].val += x * (r - l + 1);
        return;
    }
    pushdown(root);
    ll mid=(c[root].left+c[root].right)/2;
    if(mid<l)
    {
        update_tree(l,r,root*2+1,x);
    }
    else if(mid>=r)
    {
        update_tree(l,r,root*2,x);
    }
    else
    {
        update_tree(l,mid,root*2,x);
        update_tree(mid+1,r,root*2+1,x);
    }
    c[root].val=c[root*2].val+c[root*2+1].val;
}

ll sum[100005];
int main()
{
    ll n,m,q;
    while(~scanf("%lld%lld%lld",&m,&n,&q))
    {
        memset(c,0,sizeof(c));
        build_tree(1,m,1);
        ll a,b,x;
        for(int i=0; i<n; i++)
        {
            scanf("%lld%lld%lld",&a,&b,&x);
            update_tree(a,b,1,-x);
        }
        sum[0]=0;
        for(int i=1;i<=m;i++)
        {
            if(search_tree(i,i,1)<0)
            {
                //printf("%lld**\n",search_tree(i,i,1));
                sum[i]=sum[i-1]+1;
            }
            else
            {
                sum[i]=sum[i-1];
            }
        }
        sum[m+1]=sum[m];
        ll l,r;
        for(int i=1;i<=q;i++)
        {
            scanf("%lld%lld",&l,&r);
            printf("%lld\n",(sum[r]-sum[l-1]));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值