Codeforces Round #365 (Div. 2) D (线段树)

本文介绍了一种解决区间[L,R]内元素异或和问题的方法,通过预处理和线段树实现高效查询。具体地,针对每个查询,输出[L,R]区间内出现偶数次的数的异或结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

D. Mishka and Interesting sum
time limit per test
3.5 seconds
memory limit per test
256 megabytes

题意:

给出序列a1,a2,a3......an,有m次形如[L,R]的提问,对于每次提问:

输出在aL......aR这些数中出现了偶数次的所有数的异或起来的结果。

Input

The first line of the input contains single integer n (1 ≤ n ≤ 1 000 000) — the number of elements in the array.

The second line of the input contains n integers a1, a2, ..., an (1 ≤ ai ≤ 109) — array elements.

The third line of the input contains single integer m (1 ≤ m ≤ 1 000 000) — the number of queries.

Each of the next m lines describes corresponding query by a pair of integers l and r (1 ≤ l ≤ r ≤ n) — the bounds of query segment.

Output

Print m non-negative integers — the answers for the queries in the order they appear in the input.

Examples
input
3
3 7 8
1
1 3
output
0
input
7
1 2 1 3 3 2 3
5
4 7
4 5
1 3
1 7
1 5
output
0
3
1
3
2


分析:
对于区间[L,R]:
设XOR_SUM是[L,R]中所有数的"异或和",SUM表示[L,R]中出现过的数字的“异或和”。
容易发现:ANS=SUM^XOR_SUM.
问题也就变成了求SUM([L,R]中出现过的数字的“异或和”)。
在线算法没有想出来,考虑一下离线:
将提问的区间按左端点排序,先暴力求出形如[1,R]提问的所有答案,存在SUM[]中。
记Next[i]表示和i这个数相同的下一个位置。
如果现在询问变成了[2,R],在a1这个数再次出现以前(Next[a1]),SUM[1]~SUM[Next[a1]-1]这一段都要去掉a1这个数,区间修改。
答案就是点查询。
代码中sum和lazy标记共用了一个数组,代码如下:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
const int maxn=1000000+5;

int n,m,s[maxn],Next[maxn],xor_sum[maxn],ans[maxn];
map<int,int> fst;
map<int,bool> vis;
int y1,y2,val[maxn],sum[maxn<<3]; 

inline void _read(int &x){
	char ch=getchar(); bool mark=false;
	for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
	for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
	if(mark)x=-x;
}

struct Query{
	int L,R,id;
	bool operator < (const 	Query s)const {
		return L<s.L;
	} 
}ask[maxn];

void build(int o,int L,int R){
	if(L==R)sum[o]=val[L];
	else {
		int mid=(L+R)>>1;
		build(o*2,L,mid); build(o*2+1,mid+1,R);
	}
}

void pushdown(int o){
	int ls=o<<1,rs=ls+1;
	sum[ls]^=sum[o];
	sum[rs]^=sum[o];
	sum[o]=0;
}

void update(int o,int L,int R,int v){
	if(sum[o]&&L!=R)pushdown(o);
	if(y1<=L&&y2>=R) sum[o]^=v;
	else {
		int mid=(L+R)>>1;
		if(y1<=mid)update(o<<1,L,mid,v);
		if(y2>mid) update(o*2+1,mid+1,R,v);
	}
}

int query(int o,int L,int R,int p){
	if(sum[o]&&L!=R)pushdown(o);
	if(L==R) return sum[o];
	else {
		int mid=(L+R)>>1;
		if(p<=mid) return query(o<<1,L,mid,p);
		else return  query(o*2+1,mid+1,R,p); 
	}
}

int main(){
	_read(n);
	int i,x;
	for(i=1;i<=n;i++){
		_read(s[i]);
		val[i]=val[i-1];
		if(!vis[s[i]]){  //暴力求出[1,R]的答案 
			vis[s[i]]=true;
			val[i]^=s[i];
		}
		xor_sum[i]=xor_sum[i-1]^s[i];
	}
	for(i=n;i>0;i--)
		Next[i]=fst[s[i]],fst[s[i]]=i;
	build(1,1,n);
	_read(m);
	for(i=1;i<=m;i++){
		_read(ask[i].L); _read(ask[i].R);
		ask[i].id=i;
	}
	sort(ask+1,ask+1+m);
	int cur=1;
	for(i=1;i<=m;i++){
		while(cur<ask[i].L){
			if(!Next[cur])Next[cur]=n+1;
			y1=cur; y2=Next[cur]-1;
			update(1,1,n,s[cur]);
			cur++;
		}
		y1=ask[i].L; y2=ask[i].R;
		ans[ask[i].id]=query(1,1,n,y2)^xor_sum[y2]^xor_sum[y1-1];
	}
	for(i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}

<pre name="code" class="cpp">#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
const int maxn=1000005;
int sum1[maxn],sum2[maxn];
int a[maxn];
int n,m;
map<int,bool>mark;
map<int,int>last;
int _next[maxn];
//sum1  chuxian
//sum2  suoyou
struct node2{
	int l,r,id,ans;
};
node2 query[maxn];
bool cmp(node2 a,node2 b){
	if(a.l==b.l)return a.r<b.r;
	else return a.l<b.l;
}
bool cmp2(node2 a,node2 b){
	return a.id<b.id;
}
struct node{
	int a,b,lazy;
}; 
node tree[maxn<<2];
void build_tree(int p,int x,int y){
	tree[p].a=x;tree[p].b=y;
	if(x<y){
		int mid=(x+y)>>1;
		build_tree((p<<1),x,mid);
		build_tree((p<<1)+1,mid+1,y);
	}
	else{
		tree[p].lazy=sum1[x];
	}
}
void putdown(int p){
	int ls=(p<<1),rs=(p<<1)+1;
	tree[ls].lazy^=tree[p].lazy;
	tree[rs].lazy^=tree[p].lazy;
	tree[p].lazy=0;
}
void change(int p,int x,int y,int d){
	if(tree[p].b<x||tree[p].a>y)return;
	if(tree[p].a<tree[p].b&&tree[p].lazy)putdown(p);
	if(x<=tree[p].a&&tree[p].b<=y){
		tree[p].lazy^=d;
		return;
	}
	change((p<<1),x,y,d);
	change((p<<1)+1,x,y,d);
}
int getans(int p,int r){
	//if(tree[p].b<r||tree[p].a>r)return;
	if(tree[p].a<tree[p].b&&tree[p].lazy)putdown(p);
	if(tree[p].a==tree[p].b){
		return tree[p].lazy;
	}
	if(tree[p<<1].a<=r&&tree[p<<1].b>=r)getans((p<<1),r);
	else if(tree[(p<<1)+1].a<=r&&tree[(p<<1)+1].b>=r)getans((p<<1)+1,r);
}
int main(){
	int i,j,k;
	cin>>n;
	for(i=1;i<=n;i++){
		scanf("%d",&a[i]);
		if(mark[a[i]]==false){
			sum1[i]=sum1[i-1]^a[i];
			mark[a[i]]=true;
		}
		else sum1[i]=sum1[i-1];
		sum2[i]=sum2[i-1]^a[i]; 
	}
	build_tree(1,1,n);
	for(i=n;i;i--){
		_next[i]=last[a[i]];
		last[a[i]]=i;
	}
	cin>>m;
	for(i=1;i<=m;i++){
		scanf("%d%d",&query[i].l,&query[i].r);
		query[i].id=i;
	}
	sort(query+1,query+1+m,cmp);
	int l=1;
	for(i=1;i<=m;i++){
		while(l<query[i].l){
			if(_next[l]==0)_next[l]=n+1;
			change(1,l,_next[l]-1,a[l]);
			l++;
		}
		query[i].ans=getans(1,query[i].r)^sum2[query[i].r]^sum2[query[i].l-1];
	}
	sort(query+1,query+1+m,cmp2);
	for(i=1;i<=m;i++){
		printf("%d\n",query[i].ans);
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值