题目
思路
整除操作,可以考虑暴力。问题在于,我们要怎样维护可以快速
b
i
t
a
n
d
\rm bitand
bitand 的结构,使得整除之后暴力 pushUp
的速度不算太慢?
很容易想到拆位,每一位记录一下,有多少个数在这一位上是 1 1 1 。那么做 b i t a n d \rm bitand bitand 需要 O ( log V ) \mathcal O(\log V) O(logV),整除之后修改结果也是 O ( log V ) \mathcal O(\log V) O(logV) 。整除过程中,每个节点势能为 O ( log V ) \mathcal O(\log V) O(logV),所以分析出上界为 O ( n log 2 V ) \mathcal O(n\log^2 V) O(nlog2V) 。而 log V \log V logV 达到了惊人的 128 128 128,根本过不了。
这时候,我们想到一个神奇的 “分块”,那就是 b i t s e t \rm bitset bitset 。同时对 ω \omega ω 位进行操作,却是 O ( 1 ) \mathcal O(1) O(1) 的时间,这是一个 b u g \rm bug bug 般的存在!因为它真的是 让暴力变快,而不是减少冗余信息计算!
于是考虑将那 log V \log V logV 个二进制位,用 b i t s e t \rm bitset bitset 实现 “变为零” 的操作。但是 b i t s e t \rm bitset bitset 又只针对 0 , 1 0,1 0,1 啊!所以我们对 值(因变量) 进行拆位。有一种比较形象的说法是,将第 x x x 个 bit \text{bit} bit 的出现次数 c x c_x cx 用二进制写出来(写在一列上),那么原本我们是按照列存储,现在变为按照行存储,因为行数更少。
用
r
x
r_x
rx 状压存储出现次数的
x
-bit
x\text{-bit}
x-bit 为
1
1
1 的比特位。
b
i
t
a
n
d
\rm bitand
bitand 操作就是对每个
r
x
r_x
rx 进行按位与。时间复杂度
O
(
log
L
)
\mathcal O(\log L)
O(logL),其中
L
L
L 为区间长度。而 pushUp
可以用大整数加法的方式实现(相当于并行,毕竟进位也是
0
,
1
0,1
0,1,也可以用
b
i
t
s
e
t
\rm bitset
bitset 存储),也是
O
(
log
L
)
\mathcal O(\log L)
O(logL) 。
复杂度是什么呢?老规矩,势能。一个被完全覆盖的节点要往下递归,最多 O ( log V ) \mathcal O(\log V) O(logV) 次。整棵树的代价和 T ( n ) = 2 T ( n 2 ) + O ( log n ) = O ( n ) T(n)=2T({n\over 2})+\mathcal O(\log n)=\mathcal O(n) T(n)=2T(2n)+O(logn)=O(n),所以势能带来的代价是 O ( n log V ) \mathcal O(n\log V) O(nlogV),加上一般复杂度 O ( q log 2 n ) \mathcal O(q\log^2 n) O(qlog2n) 可过。
代码
容易编译超时,建议 Clang
编译。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar())
if(c == '-') f = -f;
for(; isdigit(c); c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
typedef __uint128_t ulllong;
void readHex(ulllong &v){
static int8_t haxi[1<<8];
static bool done = false; if(!done){
memset(haxi,-1,1<<8); // not good
done = true; rep(i,0,9) haxi[i^48] = int8_t(i);
rep(i,'a','f') haxi[i] = int8_t(i-'a'+10);
}
v = 0; int c = getchar(); // read by hex
for(; ~haxi[c]; c=getchar()) v = (v<<4)^haxi[c];
}
void writeHex(const ulllong &v){
if(v>>4) writeHex(v>>4);
if((v&15) < 10) putchar(int((v&15)^48));
else putchar(int((v&15)-10+'a'));
}
const int LOST = 1<<19; ///< length of segment tree
ulllong a[LOST|1]; ///< original array
template < int dep > ///< log(len)+1
struct Node{
Node<dep-1> lson, rson;
ulllong v[dep], sum, tag;
Node():tag(~ulllong(0)){}
void operator &= (const ulllong &w){
tag &= w, sum = 0;
for(int i=0; i!=dep; ++i)
v[i] &= w, sum += v[i]<<i;
}
void pushDown(){
lson &= tag, rson &= tag;
tag = compl ulllong(0);
}
void pushUp(){
ulllong &co = v[dep-1] = 0;
for(int i=0; i!=dep-1; ++i){
v[i] = lson.v[i] xor co xor rson.v[i];
co = (lson.v[i]&(co|rson.v[i]))|(co&rson.v[i]);
}
sum = lson.sum+rson.sum;
}
void build(int l=1,int r=LOST){
lson.build(l,(l+r)>>1), rson.build((l+r+2)>>1,r);
pushUp(); // update sum and v
}
void div(int ql,int qr,const ulllong &qv,int l=1,int r=LOST){
if(!sum || qr < l || r < ql) return ; // empty
pushDown(), lson.div(ql,qr,qv,l,(l+r)>>1);
rson.div(ql,qr,qv,(l+r+2)>>1,r), pushUp();
}
void band(int ql,int qr,const ulllong &qv,int l=1,int r=LOST){
if(qr < l || r < ql) return ;
if(ql <= l && r <= qr) return void(*this &= qv);
pushDown(), lson.band(ql,qr,qv,l,(l+r)>>1);
rson.band(ql,qr,qv,(l+r+2)>>1,r), pushUp();
}
ulllong query(int ql,int qr,int l=1,int r=LOST){
if(qr < l || r < ql) return ulllong(0);
if(ql <= l && r <= qr) return sum;
pushDown(); return lson.query(ql,qr,l,(l+r)>>1)
+ rson.query(ql,qr,(l+r+2)>>1,r);
}
};
template < > // specialization
struct Node<1>{
ulllong sum, *v;
Node():sum(0),v(&sum){}
void operator &= (const ulllong &w){
sum &= w; // the same as v[0]
}
void div(int ql,int qr,const ulllong &qv,int l,int r){
if(ql <= l && r <= qr) sum /= qv;
}
void band(int ql,int qr,const ulllong &qv,int l,int r){
if(ql <= l && r <= qr) sum &= qv;
}
void build(int l,int r){ sum = a[l]; }
ulllong query(int ql,int qr,int l,int r){
return (ql <= l && r <= qr) ? sum : 0;
}
};
Node<20> sgt;
int main(){
int n = readint(), q = readint();
rep(i,1,n) readHex(a[i]);
sgt.build();
for(int op,l,r; q; --q){
op = readint(), l = readint(), r = readint();
if(op == 3){
writeHex(sgt.query(l,r));
putchar('\n'); continue;
}
ulllong v; readHex(v);
if(op == 1 && v != 1) sgt.div(l,r,v);
else if(op == 2) sgt.band(l,r,v);
}
return 0;
}