“玲珑杯”ACM比赛 Round #21 【线段树标记】

1164 - 战舰萝莉
DESCRIPTION
在大战之后,法力浮·鳝AK迅速驾船驶向北海。当然他知道水之灵不可能那么容易得到,正如爱迪生曾说过:“要把BOSS打倒就要准备足够的等级。”所以鳝氪金招募了一队海盗以防战斗。
果不其然,正当鳝看见了水之灵的所在时,一大队舰娘(雾),一大队舰船突然闪现,大战一触即发。
大战前,鳝被对面的boss——北方栖姬吸引了,北方栖姬是一个可爱的小萝莉,实力却很是强劲,而鳝打算把她诱拐回家(雾),打算把她策略击破。
鳝通过提督的观察,发现北方栖姬摆出了“线段树阵形”,具体是这样的:
可以将深海栖舰的舰队看作对一个长度为n的序列建成的线段树,线段树的每个节点维护区间和,记作sumv[o]。这个值同时也是该节点的敌舰所具有的火力。同时在作战过程中,北方栖姬会不断发出区间加和区间减的指令,请注意,这些指令是以打标记的形式维护的,换而言之,某些点的sumv可能不会及时更新。
在每次敌舰队发生变化之后,鳝会问你,当前敌舰队有多少舰是“有威胁”的,我们称一艘舰是有威胁的当且仅当这艘舰的火力>k(一个给定常数)。
作为鳝船上的一名海盗,您需要回答法力浮鳝的所有询问。
下面给出敌舰队的建成,修改的具体示例代码:
http://paste.ubuntu.com/25598285/

INPUT
输入的第一行三个整数n,m,k,m表示操作次数。
接下来一行n个整数,表示线段树维护的原序列。
接下来m行,每行四个整数opt,l,r,x
如果opt=1表示区间加,反之则是区间减。表示在[l,r]加上或者减去x
OUTPUT
共m行,每行一个整数表示每次操作后的答案。
SAMPLE INPUT
8 2 20
1 2 3 4 5 6 7 8
2 1 8 100
1 1 3 20
SAMPLE OUTPUT
1
0
HINT
1 ≤ n ,m≤ 100000,序列值,k和sumv[o]始终在int范围内,可能出现负数

题解:
直接把标记过程改一下,每次修改了多少节点,相应总数量变化一下即可。

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int N=1e5+9;
#define lson (o<<1)
#define rson (o<<1|1)

int sumv[N<<2],addv[N<<2];
int a[N];
int k;
inline int pushup(int o){
    int t=sumv[o];
    sumv[o]=sumv[lson]+sumv[rson];
    if(t<=k&&sumv[o]>k)return 1;
    else if(t>k&&sumv[o]<=k)return -1;
    return 0;
}
inline int pushdown(int o,int l,int r)
{
     if(!addv[o])return 0;
     int mid=(l+r)>>1,tag=addv[o];
     addv[lson]+=tag;
     addv[rson]+=tag;
     int sl=sumv[lson],sr=sumv[rson];
     sumv[lson]+=tag*(mid-l+1);
     sumv[rson]+=tag*(r-mid);
     addv[o]=0;
     int ans=0;
     if(sl<=k&&sumv[lson]>k)ans++;
     else if(sl>k&&sumv[lson]<=k)ans--;
     if(sr<=k&&sumv[rson]>k)ans++;
     else if(sr>k&&sumv[rson]<=k)ans--;
     return ans;
 }
inline int build(int o,int l,int r){
     if(l==r){
        sumv[o]=a[l];
        return a[l]>k;
    }
     int mid=(l+r)>>1;
     int sum=0;
     sum+=build(lson,l,mid);
     sum+=build(rson,mid+1,r);
     sumv[o]=sumv[lson]+sumv[rson];
     sum+=(sumv[o]>k);
     return sum;
}
inline int optadd(int o,int l,int r,int ql,int qr,int v)
{
     if(ql<=l&&r<=qr){
        int t=sumv[o];
        sumv[o]+=v*(r-l+1);
        addv[o]+=v;
        if(sumv[o]>k&&t<=k)return 1;
        else if(sumv[o]<=k&&t>k)return -1;
        return 0;
    }
     int mid=(l+r)>>1;
     int sum=0;
     sum+=pushdown(o,l,r);     
     if(ql<=mid)sum+=optadd(lson,l,mid,ql,qr,v);     
     if(qr>mid)sum+=optadd(rson,mid+1,r,ql,qr,v);
     sum+=pushup(o);
     return sum;
}

int main()
{
    int n,m;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);

    int sum=build(1,1,n);
    int op,l,r,v;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%d",&op,&l,&r,&v);
        if(op!=1)v=-v;
        sum+=optadd(1,1,n,l,r,v);
        printf("%d\n",sum);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值