UVALive 8518 - Sum of xor sum
做法:线段树维护:答案,边界在左端点的区间异或为1的个数,边界在右端点异或为1的个数,1的个数,区间长度,这样已经自洽了。(每次讲线段树,都会讲这个题,比较经典的思想)
----update:2018/10/09
首先,我们考虑拆位,分别计算每一位的贡献然后合并出答案,现在序列的元素只包含0或1。我们希望用线段树维护,异或为1的区间的个数,即包含奇数个1的区间的个数。那么,假设现在已知,[L,mid]有多少个区间包含奇数个1,[mid+1,R]有多少个区间包含奇数个1,那么合并后的区间的答案,就是左边的答案+右边的答案+横跨两个区间的答案。
横跨两个区间的答案 = (左边以mid为右端点包含奇数个1的区间的数目) * (右边以(mid+1)为左端点包含偶数个1的区间的数目) + (左边以mid为右端点包含偶数个1的区间的数目) * (右边以(mid+1)为左端点包含奇数个1的区间的数目)
那么现在就需要我们维护上边这4个东西,显然如果知道奇数的贡献,就很容易可以计算出偶数贡献。所以实际只用维护2个东西。这两个东西也可以用类似的思想维护出来,所以我们不用维护其他的东西了。引用大佬的话:“这类题目就是需要什么维护什么,直到数据结构自洽”当然,如果发现需要维护的东西过多,就要考虑换个思路了。
#include <bits/stdc++.h>
#define pb push_back
typedef long long ll;
const int N = 1e5 + 7;
const int inf = 0x3f3f3f3f3f;
const int mod = 1e9 + 7;
using namespace std;
int T, n, q, a[N];
struct node{
ll ans[20], lx[20], rx[20];
int num[20], len;
} tree[N<<2];
node mer(node &a,node &b) {
node res;
for(int i = 0; i < 20; ++i) {
res.ans[i] = a.ans[i] + b.ans[i];
res.ans[i] += a.lx[i]*(b.len - b.rx[i]);
res.ans[i] += (a.len - a.lx[i])*b.rx[i];
if(b.num[i] & 1) res.lx[i] = b.lx[i] + (a.len - a.lx[i]);
else res.lx[i] = b.lx[i] + a.lx[i];
if(a.num[i] & 1) res.rx[i] = a.rx[i] + (b.len - b.rx[i]);
else res.rx[i] = a.rx[i] + b.rx[i];
res.num[i] = a.num[i] + b.num[i];
}
res.len = a.len + b.len;
return res;
}
void build(int p, int l, int r) {
if(l == r) {
tree[p].len = 1;
for(int i = 0; i < 20; ++i)
if( a[l] & (1<<i) ) tree[p].ans[i] = tree[p].lx[i] = tree[p].rx[i] = tree[p].num[i] = 1;
else tree[p].ans[i] = tree[p].lx[i] = tree[p].rx[i] = tree[p].num[i] = 0;
return;
}
int mid = (l + r) >> 1;
build(p<<1, l, mid); build(p<<1|1, mid+1, r);
tree[p] = mer(tree[p<<1],tree[p<<1|1]);
}
node ask(int p,int l,int r,int L,int R) {
if(l == L && R == r) {
return tree[p];
}
int mid = (l + r) >> 1;
if(R <= mid) return ask(p<<1,l,mid,L,R);
else if(L > mid) return ask(p<<1|1,mid+1,r,L,R);
else {
node tmp1 = ask(p<<1,l,mid,L,mid);
node tmp2 = ask(p<<1|1,mid+1,r,mid+1,R);
return mer(tmp1,tmp2);
}
}
int main() {
scanf("%d", &T);
while( T-- ) {
scanf("%d%d",&n,&q);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
build(1,1,n);
while(q--) { int l, r;
scanf("%d %d",&l,&r);
node tmp = ask(1,1,n,l,r);
ll ans = 0;
for(int i = 19; i >= 0; --i)
ans = ((ans<<1LL)%mod + tmp.ans[i])%mod;
printf("%lld
",ans);
}
}
return 0;
}