NKOI 3732 wjj的01序列

P3732wjj 的零一序列
时间限制 : - MS   空间限制 : 265536 KB 
评测说明 : 时限1000ms
问题描述

wjj 在 nodgd 的序列店里买了一个长度为n的0,1序列{a1,a2,…,an},想拿来做数学题。
定义f(l,r)为序列第l位到序列第r位的数字异或和。有三种操作:
• 0  L  R 询问有多少个l,r满足L≤l≤r≤R,使得f(l,r)=0。
• 1  L  R 询问有多少个l,r满足L≤l≤r≤R,使得f(l,r)=1。
• 2  x    将位置x的数取反。
其中 1≤L≤R≤n, 1≤x≤n。 你需要给出对序列进行q次上述的操作的结果。 

输入格式

第一行包含一个正整数n,表示序列的长度。
第二行包含n个整数,表示序列中的数,数字只会为0或1。
第三行包含一个正整数q,表示操作的数量。
接下来q行,每行一种操作,格式如题目所述。 

输出格式

输出多行,其中对于每一种询问操作输出一行,包含一个非负整数,表示相应的答案。 

样例输入

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

样例输出

3
6

提示


#include<cstdio>
#include<iostream>
#include<cstring>
#define LL long long
using namespace std;
const int maxn=100005;
int sum[maxn],tot,a,n,b,t,d,ans;
struct wk{int a,b,left,right,v,lazy;}tree[maxn<<2];
inline void _read(int &x){
	char t=getchar();bool sign=true;
	while(t<'0'||t>'9')
	{if(t=='-')sign=false;t=getchar();}
	for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
	if(!sign)x=-x;
}
void buildtree(int x,int y){
	int r=++tot;
	tree[r].a=x,tree[r].b=y;
	if(x<y){
		int mid=(x+y)>>1;
		tree[r].left=tot+1;
		buildtree(x,mid);
		tree[r].right=tot+1;
		buildtree(mid+1,y);
		tree[r].v=tree[tree[r].left].v+tree[tree[r].right].v;
	}
	else tree[r].v=sum[x];
}
void down(int r){
	int ls=tree[r].left,rs=tree[r].right,mid=(tree[r].a+tree[r].b)>>1;
	tree[ls].lazy^=tree[r].lazy;
	tree[rs].lazy^=tree[r].lazy;
	tree[ls].v=mid-tree[r].a+1-tree[ls].v;
	tree[rs].v=tree[r].b-mid-tree[rs].v;
	tree[r].lazy=0;
}
void add(int r){
	if(tree[r].a>b||tree[r].b<a)return;
	if(tree[r].lazy)down(r);
	if(tree[r].a>=a&&tree[r].b<=b){
		tree[r].lazy^=1;
		tree[r].v=tree[r].b-tree[r].a+1-tree[r].v;
		return;
	}
	int ls=tree[r].left,rs=tree[r].right,mid=(tree[r].a+tree[r].b)>>1;
	if(a<=mid)add(ls);
	if(b>mid)add(rs);
	tree[r].v=tree[ls].v+tree[rs].v;
}
int ask(int r){
	if(tree[r].lazy)down(r);
	int mid=(tree[r].a+tree[r].b)>>1;
	int ls=tree[r].left,rs=tree[r].right;
	if(tree[r].a>=a&&tree[r].b<=b)return tree[r].v;
	int temp=0;
	if(a<=mid)temp+=ask(ls);
	if(b>mid)temp+=ask(rs);
	return temp;
}
LL getans(int t){
	int cnt1=ask(1);
	int cnt0=b-a+1-cnt1;
	if(t==0){
		LL ans=0;
		if(cnt1>1)ans+=(LL)(cnt1-1)*cnt1/2;
		if(cnt0>1)ans+=(LL)(cnt0-1)*cnt0/2;
		return ans;
	}
	return (LL)cnt1*cnt0;
}
int main(){
	int q,x;
	_read(n);
	for(int i=1;i<=n;i++){
		_read(x);
		sum[i]=sum[i-1]^x;
	}
	buildtree(0,n);
	_read(q);
	while(q--){
		_read(t);
		if(t==2) {
			_read(d);
			a=d,b=n;
			add(1);
		}
		else{
			_read(a);_read(b);
			a--;
			cout<<getans(t)<<endl;
		}
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值