2021杭电多校第八场
Counting Stars
题意
给定一个长度为n
的数组a
,有Q
次操作,操作有
3
3
3种类型。
- 给定
l
和r
, ∑ i = r r a i \sum_{i=r}^{r} a_i ∑i=rrai。 - 给定
l
和r
, a i − l o w b i t ( a i ) , i ∈ [ l , r ] a_i-lowbit(a_i),\ i\in[l,r] ai−lowbit(ai), i∈[l,r]。 - 给定
l
和r
, a i + h i g h b i t ( a i ) , i ∈ [ l , r ] a_i+highbit(a_i),\ i\in[l,r] ai+highbit(ai), i∈[l,r]。
l
o
w
b
i
t
(
x
)
lowbit(x)
lowbit(x)计算
x
x
x二进制的最低位的1
,
h
i
g
h
b
i
t
(
x
)
highbit(x)
highbit(x)计算
x
x
x二进制的最高位的1
。
思路
考虑第2
个操作,如果将
a
i
a_i
ai的二进制1
的个数记录一个
c
n
t
cnt
cnt,每次
a
i
−
l
o
w
b
i
t
(
a
i
)
a_i-lowbit(a_i)
ai−lowbit(ai)相当于
c
n
t
−
1
cnt-1
cnt−1。
(一个数最多可以减去30次,可以直接遍历到叶子节点,如果当前节点的
c
n
t
cnt
cnt为0就不向下搜,复杂度我不会计算)
例如: x = 6 , c n t = 2 ; ⇒ x − l o w b i t ( x ) = 4 , c n t = 1 ; x=6,cnt=2;\Rightarrow x-lowbit(x)=4,cnt=1; x=6,cnt=2;⇒x−lowbit(x)=4,cnt=1;
考虑第3
个操作,每次
a
i
+
h
i
g
h
b
i
t
(
a
i
)
a_i+highbit(a_i)
ai+highbit(ai),不会影响
c
n
t
cnt
cnt的个数,只会影响二进值最高位的1
。
例如: x = 6 , c n t = 2 ; ⇒ x + h i g h b i t ( x ) = 10 , c n t = 2 ; x=6,cnt=2;\Rightarrow x+highbit(x)=10,cnt=2; x=6,cnt=2;⇒x+highbit(x)=10,cnt=2;
如果将一个数x
的二进制位最高位的1
拿出来(
u
p
p
e
r
upper
upper), 6(110
)=4(100
)+2(010
),
6
+
h
i
g
h
b
i
t
(
6
)
6+highbit(6)
6+highbit(6)=8(1000
)+2(010
),相当于
4
×
2
=
8
4\times 2=8
4×2=8,即最高位1
向前移动一位(
u
p
p
e
r
×
2
upper\times 2
upper×2)。
经过上述转换后,操作2
就变成了区间
c
n
t
−
1
cnt-1
cnt−1,操作3
变成了区间
u
p
p
e
r
×
2
upper \times 2
upper×2。
==注意!==当 c n t = 0 cnt=0 cnt=0时,需要将 u p p e r upper upper也变成 0 0 0。
AC的代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 998244353, N = 1e5 + 10;
ll a[N], n;
ll two_power[N];
void prepare() { // 预处理2的次幂
two_power[0] = 1;
for (int i = 1; i <= N - 2; i++) {
two_power[i] = (two_power[i - 1] * 2) % mod;
}
}
ll highbit(ll x) { // 找到x的最高位
for (int i = 30; i >= 0; i--)
if ((1 << i) & x) return (1 << i);
return 0;
}
int lowbit(int x) { return x & -x; }
struct Tree {
/* lz是懒标记,upper记录最高位,lower记录低位,cnt记录最多可以减去lowbit的次数
*/
int l, r;
ll lz;
ll upper, lower, cnt;
} tr[N * 4];
void pushup(int p) {
tr[p].cnt = max(tr[p << 1].cnt, tr[p << 1 | 1].cnt); // 最多的能减
tr[p].upper = (tr[p << 1].upper + tr[p << 1 | 1].upper) % mod;
tr[p].lower = (tr[p << 1].lower + tr[p << 1 | 1].lower) % mod;
}
void pushdown(int p) { // 下传标签
auto &root = tr[p], &left = tr[p << 1], &right = tr[p << 1 | 1];
if (root.lz) {
left.lz += root.lz;
left.upper = (left.upper * two_power[root.lz]) % mod;
right.lz += root.lz;
right.upper = (right.upper * two_power[root.lz]) % mod;
root.lz = 0;
}
}
void build(int p, int l, int r) {
tr[p] = {l, r, 0, 0, 0, 0};
if (l == r) {
tr[p].cnt = __builtin_popcount(a[l]); // 二进制1的个数
tr[p].upper = highbit(a[l]);
tr[p].lower = a[l] - tr[p].upper;
return;
}
int mid = l + r >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
// cnt--
void modify1(int p, int l, int r) {
if (tr[p].cnt == 0) { // 如果这个区间已经没有可以减少的cnt,就不再向下遍历
return;
}
if (tr[p].l == tr[p].r) {
tr[p].cnt--;
tr[p].lower -= lowbit(tr[p].lower);
if (tr[p].cnt == 0) // 当cnt为0时,这个值就为0了
tr[p].upper = 0;
return;
}
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) modify1(p << 1, l, r);
if (r > mid) modify1(p << 1 | 1, l, r);
pushup(p);
}
// upper*2
void modify2(int p, int l, int r) { // 操作3相当于区间内的upper全部*2
if (tr[p].l >= l && tr[p].r <= r) {
tr[p].upper = (tr[p].upper * 2) % mod;
tr[p].lz++;
return;
}
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) modify2(p << 1, l, r);
if (r > mid) modify2(p << 1 | 1, l, r);
pushup(p);
}
ll query(int p, int l, int r) { // 求区间和
if (tr[p].l >= l && tr[p].r <= r) return (tr[p].upper + tr[p].lower) % mod;
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
ll res = 0;
if (l <= mid) res = (res + query(p << 1, l, r)) % mod;
if (r > mid) res = (res + query(p << 1 | 1, l, r)) % mod;
return res % mod;
}
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin.exceptions(ios::badbit | ios::failbit);
prepare();
int T, Q;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
cin >> Q;
while (Q--) {
int op, l, r;
cin >> op >> l >> r;
if (op == 1)
cout << query(1, l, r) << endl;
else if (op == 2)
modify1(1, l, r);
else
modify2(1, l, r);
}
}
return 0;
}