题目
Problem 858: 区间黑白和
Time Limit: 1000 ms Memory Limit: 131072 KBProblem 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 1Sample 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);
}
}