【线段树】[CodeForces - 717F]Heroes of Making Magic III

题目大意

一个长度为n的序列,每一个位置都有一些小怪。英雄可以在序列上左右移动,并且可以击杀一个他所到达的位置上的小怪,每次移动必须击杀小怪。
有两种操作:

  • 1 a b k 区间[a,b]中的每一个位置都增加k个小怪
  • 2 a b 英雄能否在一个端点开始,在另一个端点结束,并且杀光[a,b]内所有小怪,英雄不能移动出区间。

分析

设第 i 个位置有ai个小怪
假设区间为 [l,r] ,我们让英雄从左端点开始,向右移动。假设英雄当前在 i ,我们让英雄在i,i+1之间徘徊,直到把第 i 个位置的小怪都杀光。
如果英雄从右端点开始,实际上就是把从左端点开始的移动反过来。

考虑这种情况我们需要哪些条件:
al1
al+1al0
al+1(al+1al+1)0al+1al+1+al>=1

al+ial+i1+al+i2[imod2=0]
arar1+ar2[rl+1mod2=0]

考虑一下怎么维护,
diaiai1+ai2 ,那么 di+1=ai+1di

假设当前询问的区间是 [l,r]
那么,设 di=(ai+lai+l1+ai+l2)

di={di+dl1di+dl1i is eveni is odd

在做修改操作的时候
[l,r] 区间内,和l奇偶性相同的位置的 d +k
区间后,如果区间长度是偶数,就没有影响,否则,和l奇偶性相同的 +k ,不同的 k

我们用可以对奇偶位置分别维护。

代码

#include<cstdio>
#include<algorithm>
#define MAXN 200000
#define INF 0x7fffffff
using namespace std;
void Read(int &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
int n,a[MAXN+10],d[MAXN+10],m;
struct node{
    int odd,even,mio,mie;
}tree[(1<<18)+10];
inline void update(int i){
    tree[i].mio=min(tree[i<<1].mio,tree[(i<<1)|1].mio);
    tree[i].mie=min(tree[i<<1].mie,tree[(i<<1)|1].mie);
}
void build(int i,int l,int r){
    if(l==r){
        tree[i].mio=d[(l<<1)-1];
        tree[i].mie=d[l<<1];
        return;
    }
    tree[i].odd=tree[i].even=0;
    int mid((l+r)>>1);
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    update(i);
}
void read(){
    Read(n);
    int i;
    for(i=1;i<=n;i++){
        Read(a[i]);
        d[i]=a[i]-d[i-1];
    }
    build(1,1,(n+1)>>1);
}
inline void push_down(int i){
    if(tree[i].even){
        tree[i<<1].even+=tree[i].even;
        tree[(i<<1)|1].even+=tree[i].even;
        tree[i<<1].mie+=tree[i].even;
        tree[(i<<1)|1].mie+=tree[i].even;
        tree[i].even=0;
    }
    if(tree[i].odd){
        tree[i<<1].odd+=tree[i].odd;
        tree[(i<<1)|1].odd+=tree[i].odd;
        tree[i<<1].mio+=tree[i].odd;
        tree[(i<<1)|1].mio+=tree[i].odd;
        tree[i].odd=0;
    }
}
void insert(int i,int l,int r,int ll,int rr,int od,int ev){
    if(ll<=l&&r<=rr){
        tree[i].even+=ev;
        tree[i].odd+=od;
        tree[i].mie+=ev;
        tree[i].mio+=od;
        return;
    }
    if(ll>r||rr<l)
        return;
    int mid((l+r)>>1);
    push_down(i);
    insert(i<<1,l,mid,ll,rr,od,ev);
    insert((i<<1)|1,mid+1,r,ll,rr,od,ev);
    update(i);
}
int get_mi_e(int i,int l,int r,int ll,int rr){
    if(ll<=l&&r<=rr)
        return tree[i].mie;
    if(ll>r||rr<l)
        return INF;
    int mid((l+r)>>1);
    push_down(i);
    return min(get_mi_e(i<<1,l,mid,ll,rr),get_mi_e((i<<1)|1,mid+1,r,ll,rr));
}
int get_mi_o(int i,int l,int r,int ll,int rr){
    if(ll<=l&&r<=rr)
        return tree[i].mio;
    if(ll>r||rr<l)
        return INF;
    int mid((l+r)>>1);
    push_down(i);
    return min(get_mi_o(i<<1,l,mid,ll,rr),get_mi_o((i<<1)|1,mid+1,r,ll,rr));
}
void solve(){
    int p,l,r,v,pre;
    Read(m);
    while(m--){
        Read(p),Read(l),Read(r);
        l++,r++;
        if(p==1){
            Read(v);
            if(l&1)
                insert(1,1,(n+1)>>1,(l+1)>>1,(r+1)>>1,v,0);
            else
                insert(1,1,(n+1)>>1,l>>1,r>>1,0,v);
            if(!((r-l)&1)){
                if(r&1){
                    insert(1,1,(n+1)>>1,(r>>1)+1,(n+1)>>1,0,-v);
                    insert(1,1,(n+1)>>1,(r>>1)+2,(n+1)>>1,v,0);
                }
                else
                    insert(1,1,(n+1)>>1,(r>>1)+1,(n+1)>>1,-v,v);
            }
        }
        else{
            if(l&1){
                if(l==1)
                    pre=0;
                else
                    pre=get_mi_e(1,1,(n+1)>>1,l>>1,l>>1);
                if(r&1){
                    if(get_mi_o(1,1,(n+1)>>1,(r+1)>>1,(r+1)>>1)+pre!=1){
                        puts("0");
                        continue;
                    }
                }
                else
                    if(get_mi_e(1,1,(n+1)>>1,r>>1,r>>1)-pre){
                        puts("0");
                        continue;
                    }
                if(get_mi_o(1,1,(n+1)>>1,(l>>1)+1,(r+1)>>1)>=1-pre&&get_mi_e(1,1,(n+1)>>1,(l+1)>>1,r>>1)>=pre)
                    puts("1");
                else
                    puts("0");
            }
            else{
                if(l==1)
                    pre=0;
                else
                    pre=get_mi_o(1,1,(n+1)>>1,l>>1,l>>1);
                if(r&1){
                    if(get_mi_o(1,1,(n+1)>>1,(r+1)>>1,(r+1)>>1)-pre){
                        puts("0");
                        continue;
                    }
                }
                else
                    if(get_mi_e(1,1,(n+1)>>1,r>>1,r>>1)+pre!=1){
                        puts("0");
                        continue;
                    }
                pre=get_mi_o(1,1,(n+1)>>1,l>>1,l>>1);
                if(get_mi_o(1,1,(n+1)>>1,(l>>1)+1,(r+1)>>1)>=pre&&get_mi_e(1,1,(n+1)>>1,(l+1)>>1,r>>1)>=1-pre)
                    puts("1");
                else
                    puts("0");
            }
        }
    }
}
int main()
{
    read();
    solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值