GalaxyOJ-858 (tag线段树)

题目

Problem 858: 区间黑白和
Time Limit: 1000 ms Memory Limit: 131072 KB

Problem Description

给出一个序列a[1..n]。对于任意位置i有两种颜色,黑色与白色(不妨以1指黑,0指白)。

请你维护两个操作。

1.求出连续区间所有某种颜色的位置的a[]和

2.翻转某连续区间所有位置的颜色(黑变白,白变黑)

Input

第一行1个整数n。
第二行n个整数描述a[]。
第三行n个整数描述每个位置的颜色。
接下来一行一个整数m指操作数。
接下来m行,每行对应一个操作,先输入type,
若type=1 ,接下来输入L,R,col(col为0或1)。求出L,R之间的所有颜色为col的a[]和(包括L,R)。
若type=2,接下来输入L,R,翻转L,R之间所有位置的颜色。

对于30%数据满足0<n,m<=1000
对于另外10%数据满足type≠2
对于100%数据满足0<n,m<=100000

Output

按顺序每行回应一个type=1操作,即每行一个整数回答询问的答案。

Sample Input

5
1 2 3 4 5
1 0 1 0 1
3
1 1 4 1
2 3 4
1 1 4 1

Sample Output

4
5

分析

  • 就是一个裸的线段树吧,tag记录一下它代表的区间是否被翻转颜色。(一遍过了,简单的线段树还算熟练啦~)

程序

#include <cstdio>
#include <iostream>
#define N 100005
using namespace std;
int n,m,k,a[N],b[N];

struct segment_tree{
    //tag 表示其子树(除当前节点)被翻转了 
    #define X (t[x])
    #define Lx (x<<1)
    #define Rx ((x<<1)+1)
    #define Mid ((l+r)>>1)
    #define L (t[Lx])
    #define R (t[Rx])
    struct node{int v[2],tag,l,r;} t[3*N];
    int A,B,C;

    void merge(int x){
        X.v[0]=L.v[0]+R.v[0];
        X.v[1]=L.v[1]+R.v[1];
    }

    void add_tag(int x){swap(X.v[0],X.v[1]); X.tag^=1;}
    void down_tag(int x){
        if (X.tag){
            add_tag(Lx);
            add_tag(Rx);
            X.tag=0;
        }
    }

    void change(int x){
        if (A<=X.l && X.r<=B){add_tag(x); return;}
        down_tag(x);
        if (A<=L.r) change(Lx);
        if (B>=R.l) change(Rx);
        merge(x);
    }

    int query(int x){
        if (A<=X.l && X.r<=B){return X.v[C];};
        down_tag(x);
        int ret=0;
        if (A<=L.r) ret+=query(Lx);
        if (B>=R.l) ret+=query(Rx);
        return ret;
    }

    void build(int x,int l,int r){
        X.l=l,X.r=r;
        if (l==r){X.v[b[l]]+=a[l]; return;}
        build(Lx,l,Mid);
        build(Rx,Mid+1,r);
        merge(x);
    }

} T;

int main(){
    freopen("1.txt","r",stdin);
    scanf("%d",&n);
    for (int i=1; i<=n; i++) scanf("%d",&a[i]);
    for (int i=1; i<=n; i++) scanf("%d",&b[i]);

    T.build(1,1,n);
    for (scanf("%d",&m); m--;){
        scanf("%d",&k);
        if (k==1){
            scanf("%d%d%d",&T.A,&T.B,&T.C);
            printf("%d\n",T.query(1));
            continue;
        }
        scanf("%d%d",&T.A,&T.B);
        T.change(1);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值