Gym 100739A Queries (线段树+拆位维护)

题目链接:https://vjudge.net/problem/Gym-100739A

                                                             Queries

XORin discovers an interesting function called Elf. XORina has given XORin an array A of N integers and Q queries. The queries are of 2 types:

1 p x - the element on the position p changes his value to x (Ap=x)

2 a b - find the sum of Elf(i,j) for each a ≤ i ≤ j ≤ b, where Elf(i,j)=Ai xor Ai + 1 xor ... xor Aj.

Your task is to print the responses to the 2nd type of queries.

Input

The first line of the input contains n, the size of the array. The second line of the input contains m, the number of queries (1 ≤ n, m ≤ 100000). The third line of the input contains n numbers, the initial elements of the array (each of them is between 1 and 1000). On the next m lines each line contains a query. The first type of query is 1 p x (1 ≤ p ≤ n and 0 ≤ x ≤ 1000). The second type of query is 2 a b (1 ≤ a ≤ b ≤ n).

Output

The output contains the answer to the second type of queries, each answer on a line. Print the answer modulo 4001.

Examples

Input

4
8
1 2 3 4
2 1 2
1 1 2
2 1 3
2 1 4
1 3 7
2 1 3
1 4 5
2 1 4

Output

6
11
34
23
32

题目大意:给你一个数组,对数组进行单点修改,查询区间内所有子区间的异或和。 

思路:利用线段树容易想到,重要的是进行拆位维护,对于每个二进制位开一个线段树 

合并:维护区间从左端开始、从右端点开始、从或不从左右端点开始(全部)的异或和为0、1的区间个数,然后进行合并  

具体详见代码

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
typedef long long ll;
const int MOD=4001;
int n,m;
const int INF=1e5+5;
int a[INF];
struct node{
	int d;//区间的异或值 
	int l,r;//区间的左右边界 
	int le[2],re[2];//以左或右端点为起点的区间异或值为1或0的个数 
	int sum[2];//区间异或值为1或0的个数 
}f[10][INF*3];//2*INF会导致内存不够,4*INF会导致内存溢出,3*INF刚好AC 
int bit[12],b[INF][10];//每个数的二进制位 
int bb[10];
void init()
{
	int i,j,cnt;
	bit[0]=1;
	for(i=1;i<=10;i++)
	{
		bit[i]=bit[i-1]*2;
	}
	for(i=1;i<=n;i++)
	{
		j=a[i];
		cnt=0;
		while(j){
			b[i][cnt++]=j%2;
			j=j/2;
		}
	}
}
//区间合并 
node push_up(int id,int ans,node u,node v)//返回node,利于查询时的区间合并 
{
	node w;
	if(ans){
		w=f[id][ans];
	}
	w.d =u.d^v.d ;//区间的值等于其左右子区间的值的异或 
	//若左子区间为1,则加上v.le[1],,因为1^1=0,下面都同理 
	w.le[0]=(u.le[0]+v.le[u.d==0?0:1])%MOD; 
	w.le[1]=(u.le[1]+v.le[u.d==0?1:0])%MOD;
	w.re[0]=(v.re[0]+u.re[v.d==0?0:1])%MOD;
	w.re[1]=(v.re[1]+u.re[v.d==0?1:0])%MOD;
	//同理0^0=0,1^1=0;进行区间合并 
	w.sum[0]=(u.sum[0]+v.sum[0]+u.re[0]*v.le[0]+u.re[1]*v.le[1])%MOD;
	w.sum[1]=(u.sum[1]+v.sum[1]+u.re[0]*v.le[1]+u.re[1]*v.le[0])%MOD;
	return w;
}
void build(int id,int ans,int l,int r)//建树 
{
	f[id][ans].l =l;f[id][ans].r =r;
	if(l==r){
		int dd=b[l][id];
		f[id][ans].d =dd;
		f[id][ans].le[dd]=f[id][ans].re[dd]=f[id][ans].sum[dd]=1;
		f[id][ans].le[dd^1]=f[id][ans].re[dd^1]=f[id][ans].sum[dd^1]=0;
		return ;
	}
	int mid=(l+r)>>1;
	build(id,ans<<1,l,mid);
	build(id,ans<<1|1,mid+1,r);
	f[id][ans]=push_up(id,ans,f[id][ans<<1],f[id][ans<<1|1]);
}
void change(int id,int ans,int l)//区间修改 
{
	if(f[id][ans].l ==f[id][ans].r &&f[id][ans].l ==l){
		int u=bb[id];
		f[id][ans].d =u;
		f[id][ans].le[u]=f[id][ans].re[u]=f[id][ans].sum[u]=1;
		f[id][ans].le[u^1]=f[id][ans].re[u^1]=f[id][ans].sum[u^1]=0;
		return ;
	}
	int mid=(f[id][ans].l+f[id][ans].r )>>1;
	if(l<=mid)
	{
		change(id,ans<<1,l);
	}
	else change(id,ans<<1|1,l);
	f[id][ans]=push_up(id,ans,f[id][ans<<1],f[id][ans<<1|1]);
}
node query(int id,int ans,int l,int r)//区间查询 
{
	if(f[id][ans].l ==l&&f[id][ans].r ==r){
		
		return f[id][ans];
	}
	int mid=(f[id][ans].l +f[id][ans].r )>>1;
	if(r<=mid){
		return query(id,ans<<1,l,r);
	}
	else if(l>mid){
		return query(id,ans<<1|1,l,r);
	}
	else{
		node u,v;
		u=query(id,ans<<1,l,mid);
		v=query(id,ans<<1|1,mid+1,r);
		return push_up(0,0,u,v);
	}
}
int main()
{
	cin>>n>>m;
	int i,j,k;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	init();
	for(i=0;i<10;i++)
	{
		build(i,1,1,n);
	}
	int ans;
	while(m--)
	{
		scanf("%d%d%d",&i,&j,&k);
		if(i==1)
		{
			int u=k,v=0;
			while(u)
			{
				bb[v++]=u%2;
				u=u/2;
			}
			for(i=0;i<v;i++)
			{
				if(b[j][i]!=bb[i]) //进行剪枝 ,降低时间复杂度 
				change(i,1,j);
				b[j][i]=bb[i];
			}
			for(i=v;i<10;i++)//注意这,要全部更新 
			{
				bb[i]=0;
				if(b[j][i]!=bb[i]) //剪枝 
				change(i,1,j);
				b[j][i]=bb[i];
			}
		}
		else if(i==2)
		{
			ans=0;
			node u;
			for(i=0;i<10;i++)
			{
				u=query(i,1,j,k);
				ans=(ans+u.sum[1]*bit[i])%MOD;
			}
			printf("%d\n",ans);
		}
	}
    return 0;	
} 

 

也可以利用l0+l1的关系,维护更少的内容,

点击链接:https://blog.csdn.net/alan_cty/article/details/50804865

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值