P12500 XOR and OR
P12500 XOR and OR
一道很好玩的题。
看到题的第一反映是拆位,拆完位就很好做了,先取反,可以转化成求所有子区间权值按位与的异或和。
维护一下前缀后缀
具体地:
但是你发现这只有
考虑优化空间,你发现我只在乎奇偶性,所以
但还是过不了,因为时间复杂度是
这时你发现,这些全是 bool 型的,真的有必要全部分开算吗?
你发现我只要能把我所有 bool 的转移换成位运算,就可以全部一起算(因为位与位直接互不影响)。
于是你不拆位了,考虑把这些东西全部一起算。
合并
再强调一次:只要能把我所有 bool 的转移换成位运算,就可以全部一起算。
node merge(node a,node b){
if(a.And0 == (1ll << 60) - 1 && a.And1 == (1ll << 60) - 1)return b;//这个无所谓,用来判掉a为空
node c;
c.pre1 = a.pre1 ^ (b.pre1 & a.And0);
c.suf1 = b.suf1 ^ (a.suf1 & b.And0);
c.pre0 = a.pre0 ^ (b.pre0 & a.And1);
c.suf0 = b.suf0 ^ (a.suf0 & b.And1);
c.ans1 = (a.ans1 ^ b.ans1 ^ (a.suf1 & b.pre1));
c.ans0 = (a.ans0 ^ b.ans0 ^ (a.suf0 & b.pre0));
c.And0 = a.And0 & b.And0;
c.And0 = a.And1 & b.And1;
return c;
}
首先: pre , suf 的转移。
c.pre1 = a.pre1 ^ (b.pre1 & a.And0);
c.suf1 = b.suf1 ^ (a.suf1 & b.And0);
c.pre0 = a.pre0 ^ (b.pre0 & a.And1);
c.suf0 = b.suf0 ^ (a.suf0 & b.And1);
假设它是 bool 你会怎么写?
c.pre1 = (a.And0 ? (a.pre1 ^ b.pre1) : a.pre1);
对吧?
翻译成人话就是如果
再看这个:
c.pre1 = a.pre1 ^ (b.pre1 & a.And0);
具体一点:
a.And0 是 1 , 那么这一位就是 (a.pre1 ^ b.pre1) 。
a.And0 是 0 , 那么这一位就是 (a.pre1 ^ (b.pre1 & 0)) 也就是 a.pre1 。
发现了吗?和上面那个是完全等价的,于是我们就成功的替换成位运算了。
其次:ans 的转移。
c.ans1 = (a.ans1 ^ b.ans1 ^ (a.suf1 & b.pre1));
c.ans0 = (a.ans0 ^ b.ans0 ^ (a.suf0 & b.pre0));
假设它是 bool 你会怎么写?
c.ans1 = (a.ans1 ^ b.ans1 ^ (a.suf1 * b.pre1));
其实在 bool 中 * 和 & 是一样的,所有直接替换就行了。
最后:And 的转移。
本来就是位运算。
下传标记
接着就是要整体异或上一个值。
因为我们处理了一个数每一位取反后的值,所有我们其实是要交换某些位。
void Swap(int &a,int &b,int x){
int v = (a & x) ^ (b & x);
a ^= v , b ^= v;
}
假设它是 bool 你会怎么写?
void Swap(bool &a,bool &b,bool x){
if(x)swap(a , b);
}
我们知道,swap 可以换成下面这样。
void Swap(bool &a,bool &b,bool x){
if(x){
int t = a ^ b;
a ^= t , b ^= t;
//a ^= b ^= a ^= b;
}
}
我们希望
于是:
void Swap(bool &a,bool &b,bool x){
int t = (x & a) ^ (x & b);
a ^= t , b ^= t;
}
有了这个东西,下传标记就很简单了。
void Xor(node &rt , int x){
Swap(rt.pre0 , rt.pre1 , x);
Swap(rt.suf0 , rt.suf1 , x);
Swap(rt.And0 , rt.And1 , x);
Swap(rt.ans0 , rt.ans1 , x);
return ;
}
void down(int u){
if(lazy[u]){
lazy[u << 1] ^= lazy[u] , lazy[u << 1 | 1] ^= lazy[u];
Xor(tree[u << 1] , lazy[u]) , Xor(tree[u << 1 | 1] , lazy[u]);
lazy[u] = 0;
}
}
于是,本来要 60 次现在用一次位运算解决了。
代码:
#include<bits/stdc++.h>
#define int long long
#define f(i , l , r) for(int i = (l);i <= (r);++ i)
#define d(i , l , r) for(int i = (r);i >= (l);-- i)
#define pii pair<int,int>
#define pb push_back
#define fi first
#define sc second
#define lowbit(x) ((x)&-(x))
#define fre(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
const int N = 5e5 + 10;
int n , q , a[N] , lazy[N<<2];
struct node{
int suf1 , pre1 , suf0 , pre0 , ans1 , ans0 , And0 , And1;
}tree[N<<2];
node merge(node a,node b){
if(a.And0 == (1ll << 60) - 1 && a.And1 == (1ll << 60) - 1)return b;
node c;
c.pre1 = a.pre1 ^ (b.pre1 & a.And0);
c.suf1 = b.suf1 ^ (a.suf1 & b.And0);
c.pre0 = a.pre0 ^ (b.pre0 & a.And1);
c.suf0 = b.suf0 ^ (a.suf0 & b.And1);
c.ans1 = (a.ans1 ^ b.ans1 ^ (a.suf1 & b.pre1));
c.ans0 = (a.ans0 ^ b.ans0 ^ (a.suf0 & b.pre0));
c.And0 = a.And0 & b.And0;
c.And0 = a.And1 & b.And1;
return c;
}
void up(int u){
tree[u] = merge(tree[u << 1] , tree[u << 1 | 1]);
}
void Swap(int &a,int &b,int x){
int v = (a & x) ^ (b & x);
a ^= v , b ^= v;
}
void Xor(node &rt , int x){
Swap(rt.pre0 , rt.pre1 , x);
Swap(rt.suf0 , rt.suf1 , x);
Swap(rt.And0 , rt.And1 , x);
Swap(rt.ans0 , rt.ans1 , x);
return ;
}
void down(int u){
if(lazy[u]){
lazy[u << 1] ^= lazy[u] , lazy[u << 1 | 1] ^= lazy[u];
Xor(tree[u << 1] , lazy[u]) , Xor(tree[u << 1 | 1] , lazy[u]);
lazy[u] = 0;
}
}
void input(int u,int l,int r,int L,int R,int x){
if(L <= l&&r <= R){
Xor(tree[u] , x);
lazy[u] ^= x;
return ;
}
int mid = l + r >> 1;down(u);
if(L <= mid)input(u << 1 , l , mid , L , R , x);
if(R > mid)input(u << 1 | 1 , mid + 1 , r , L , R , x);
return up(u);
}
node query(int u,int l,int r,int L,int R){
if(L <= l && r <= R)return tree[u];
int mid = l + r >> 1;node res = {0 , 0 , 0 , 0 , 0 , 0 , (1ll << 60) - 1 , (1ll << 60) - 1};
down(u);
if(L <= mid)res = merge(res , query(u << 1 , l , mid , L , R));
if(R > mid)res = merge(res , query(u << 1 | 1 , mid + 1 , r , L , R));
return res;
}
void build(int u,int l,int r){
if(l == r){
tree[u] = {a[l] , a[l] , a[l] ^ (1ll << 60) - 1 , a[l] ^ (1ll << 60) - 1 , a[l] , a[l] ^ (1ll << 60) - 1 , a[l] , a[l] ^ (1ll << 60) - 1};
return ;
}
int mid = l + r >> 1;
build(u << 1 , l , mid);
build(u << 1 | 1 , mid + 1 , r);
return up(u);
}
signed main(){
ios_base::sync_with_stdio(0);
cin.tie(0) , cout.tie(0);
cin >> n >> q;
f(i , 1 , n)cin >> a[i] , a[i] ^= (1ll << 60) - 1;
build(1 , 1 , n);
f(i , 1 , q){
int l , r , x , op;
cin >> op >> l >> r;
if(op == 1)cin >> x , input(1 , 1 , n , l , r , x);
else cout << (query(1 , 1 , n , l , r).ans1 ^ (((r - l + 1) * (r - l + 2) / 2 & 1) ? (1ll << 60) - 1 : 0ll)) << "\n";
}
}