题目链接:
http://codeforces.com/problemset/problem/242/E
题意:
给出一个序列,有两种操作,一种是计算l到r的和,另一种是让l到r的数全部和x做异或运算。
思路:
from: http://blog.csdn.net/u013912596/article/details/39006317
很显然直接暴力是不可能的,又是两种操作,又想到了线段树。。但是这并不简单,异或操作该怎么处理?
异或是一种位运算,如果x的第j位是1,那么说明l到r的每个数的第j位都要反转,(0^1=1,1^1=0),如果是0,那么不变。既然是位运算,那么可不可以将每一位作为线段树单独维护呢?
好像可以呢!异或操作的话,相当于是一种区间操作,只需要将l到r的某些位进行反转操作不就行了吗?反转操作什么的,打上lazy tag不就OK啦,求和操作也可以计算出l到r的每一位上有多少个1来算出最后的和。
好,那么理一下思路,首先确定建立多少个线段树,数的范围是10^6,也就是说,我们最多只需要建立20个线段树即可,1e6最多20位嘛,每一位都是一颗线段树,有n个叶子节点,再写好线段树的bulid,update,query三个函数。每个节点上需要维护两个值,一个是该区间有多少个1(用于求和),一个是该区间是否反转(lazy tag)。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define MS(a) memset(a,0,sizeof(a)) #define MP make_pair #define PB push_back const int INF = 0x3f3f3f3f; const ll INFLL = 0x3f3f3f3f3f3f3f3fLL; inline ll read(){ ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } ////////////////////////////////////////////////////////////////////////// const int maxn = 1e5+10; struct node{ int l,r,cnt,lazy; }p[20][maxn<<2]; ll n,x,two[20]; int a[maxn][20]; int build(int rt,int l,int r,int na){ if(l==r){ p[na][rt] = node{l,r,a[l][na],0}; return a[l][na]; } int mid = (l+r)/2; int res = build(rt<<1,l,mid,na); res += build(rt<<1|1,mid+1,r,na); p[na][rt] = node{l,r,res,0}; return res; } void upd(int rt,int l,int r,int na){ int L = p[na][rt].l, R = p[na][rt].r; if(L==l && R==r){ p[na][rt].lazy = 1-p[na][rt].lazy; p[na][rt].cnt = R-L+1-p[na][rt].cnt; return ; } if(p[na][rt].lazy){ p[na][rt<<1].lazy = 1-p[na][rt<<1].lazy; p[na][rt<<1].cnt = p[na][rt<<1].r-p[na][rt<<1].l+1-p[na][rt<<1].cnt; p[na][rt<<1|1].lazy = 1-p[na][rt<<1|1].lazy; p[na][rt<<1|1].cnt = p[na][rt<<1|1].r-p[na][rt<<1|1].l+1-p[na][rt<<1|1].cnt; p[na][rt].lazy = 1-p[na][rt].lazy; } int mid = (L+R)/2; if(mid >= r) upd(rt<<1,l,r,na); else if(mid < l) upd(rt<<1|1,l,r,na); else { upd(rt<<1,l,mid,na); upd(rt<<1|1,mid+1,r,na); } p[na][rt].cnt = p[na][rt<<1].cnt + p[na][rt<<1|1].cnt; } ll que(int rt,int l,int r,int na){ int L = p[na][rt].l, R = p[na][rt].r; if(l==L && r==R) return p[na][rt].cnt; if(p[na][rt].lazy){ p[na][rt<<1].lazy = 1-p[na][rt<<1].lazy; p[na][rt<<1].cnt = p[na][rt<<1].r-p[na][rt<<1].l+1-p[na][rt<<1].cnt; p[na][rt<<1|1].lazy = 1-p[na][rt<<1|1].lazy; p[na][rt<<1|1].cnt = p[na][rt<<1|1].r-p[na][rt<<1|1].l+1-p[na][rt<<1|1].cnt; p[na][rt].lazy = 1-p[na][rt].lazy; } ll res = 0; int mid = (L+R)/2; if(mid >= r) res += que(rt<<1,l,r,na); else if(mid < l) res += que(rt<<1|1,l,r,na); else res += que(rt<<1,l,mid,na)+que(rt<<1|1,mid+1,r,na); return res; } int main(){ two[0] = 1; for(int i=1; i<20; i++) two[i] = two[i-1]*2; cin >> n; for(int i=1; i<=n; i++){ cin >> x; int j = 0; while(x){ a[i][j++] = x&1; x >>= 1; } } for(int i=0; i<20; i++) build(1,1,n,i); int m = read(); while(m--){ int a,b,op = read(); if(op == 1){ scanf("%d%d",&a,&b); ll sum = 0; for(int i=0; i<20; i++) sum += que(1,a,b,i)*two[i]; printf("%I64d ",sum); }else{ int x; scanf("%d%d%d",&a,&b,&x); int i = 0; while(x){ if(x&1) upd(1,a,b,i); i++; x>>=1; } } } return 0; }