E XOR on Segment
题目大意

主要思路
由于异或和求和并没有什么性质
通常处理异或的两种方式一种为做前缀和,显然与本题没什么关系
另一种就是拆位
我们考虑讲每一位的状态作为一颗线段树,线段树维护的就是n个数每个数的第i位是1还是0
有两种操作
- 第一种求和,我们在线段树中对每一位1的个数求和乘2的多少次方就是这一位上的和,把l~r区间的所有位的和加起来即可
- 第二种对l~r区间异或x,我们也将x拆位,对于每一位而言
- 如果是0,则对l~r区间的数没有影响
- 如果是1,那么区间内所有0将变成1,所有1将变成0,也就是1和0的个数互换
于是我们线段树维护每一位上所有区间中1的个数,懒标记为改区间异或1的次数
更具体的细节看代码
AC代码
#include <bits/stdc++.h>
using namespace std;
#define debug(a) cout << #a << " = " << a << endl;
#define x first
#define y second
typedef long long ll;
const int N = 100001, mod = 1e9+7;
struct node
{
int l, r;
ll sum, lazy;//维护1的个数,和懒标记:区间异或1的次数
}tr[21][N * 4];
int n, m, a[N];
void pushup(int t, int u)
{
tr[t][u].sum = tr[t][u << 1].sum + tr[t][u << 1 | 1].sum;
}
void pushdown(int t, int u)
{
if(tr[t][u].lazy)//如果有懒标记
{
tr[t][u << 1 | 1].lazy += tr[t][u].lazy;
tr[t][u << 1].lazy += tr[t][u].lazy;
if(tr[t][u].lazy % 2)//如果父节点的异或1的次数为奇数时子节点才需修改
{
tr[t][u << 1].sum = (tr[t][u << 1].r - tr[t][u << 1].l + 1) - tr[t][u << 1].sum;
tr[t][u << 1 | 1].sum = (tr[t][u << 1 | 1].r - tr[t][u << 1 | 1].l + 1) - tr[t][u << 1 | 1].sum;
}
tr[t][u].lazy = 0;//清空
}
}
void build(int t, int u, int l, int r)
{
tr[t][u].l = l, tr[t][u].r = r;
if(l == r) tr[t][u].sum = (a[l] >> t) & 1;
else
{
int mid = l + r >> 1;
build(t, u << 1, l, mid), build(t, u << 1 | 1, mid + 1, r);
pushup(t, u);
}
}
int query(int t, int u, int l, int r)
{
if(l <= tr[t][u].l && r >= tr[t][u].r) return tr[t][u].sum;
else
{
pushdown(t, u);
int mid = tr[t][u].l + tr[t][u].r >> 1;
int res = 0;
if(l <= mid) res += query(t, u << 1, l, r);
if(r > mid) res += query(t, u << 1 | 1, l, r);
return res;
}
}
void modify(int t, int u, int l, int r)
{
if(l <= tr[t][u].l && r >= tr[t][u].r)
{
tr[t][u].lazy++;
tr[t][u].sum = (tr[t][u].r - tr[t][u].l + 1) - tr[t][u].sum;//因为只有异或1才会修改,所以一定修改sum
}
else
{
pushdown(t, u);
int mid = tr[t][u].l + tr[t][u].r >> 1;
if(l <= mid) modify(t, u << 1, l, r);
if(r > mid) modify(t, u << 1 | 1, l, r);
pushup(t, u);
}
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
cin >> m;
for(int i = 0; i < 21; i++)//每一位建立线段树
{
build(i, 1, 1, n);
}
while(m--)
{
int t, l, r;
cin >> t >> l >> r;
if(t == 1)
{
ll ans = 0;
for(int i = 0; i < 21; i++)
{
ans += ((ll)query(i, 1, l, r) << i);//查询每一位l~r中1的个数再乘2的i次方就是这一位的和
}
cout << ans << endl;
}
else
{
int x;
cin >> x;
for(int i = 0; i < 21; i++)
{
if((x >> i) & 1)//如果是1再修改当前位
{
modify(i, 1, l, r);
}
}
}
}
return 0;
}
1547



被折叠的 条评论
为什么被折叠?



