牛客练习赛4 C-Sum 线段树+二进制拆分

传送门:https://ac.nowcoder.com/acm/contest/16/C

题意

给你n个数$A_1…A_n,m$个操作。 操作分两种:

  • $操作一:1\;x\;y,将A_x改成y$
  • $操作二:2\;l\;r,求[A_l…A_r]所有子集的\&并mod\;1e9+7$

思路

这题一看就知道树状数组或者线段树去做。 因为和位运算有关,所以考虑每个数的二进制位。

比如(3)_2=11,则贡献为$2^0*(2^1-1)+2^1*(2^1-1)$

因为是$\&$操作,所以该位上必须都是1才行,所以统计该位上有多少1即可。

比如1,2,3为1,10,11,则组合起来为22,贡献为$2^0*(2^2-1)+2^1*(2^2-1)$

如何更新和查询?线段树!

只不过更新的是一个数的二进制数组而已。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
##include "bits/stdc++.h"
using namespace std;

typedef long long ll;
const ll mod = 1e9 + 7;

const int N = 4e5 + 10;

##define lc u << 1
##define rc u << 1 1
##define mid (t[u].l + t[u].r) / 2

struct Tree {
int l, r;
ll OR[55];
}t[N << 2];
ll bit[N], a[N];

inline void push_up(int u) {
for(int i = 0;i <= 32; i++) {
t[u].OR[i] = (t[lc].OR[i] + t[rc].OR[i]) % mod;
}
}

void build(int u, int l, int r) {
t[u].l = l; t[u].r = r;
if(l == r) {
for(int i = 0;i <= 32; i++) {
t[u].OR[i] += a[l] & 1;
a[l] >>= 1;
}
return ;
}
int m = (l + r) >> 1;
build(lc, l, m);
build(rc, m + 1, r);
push_up(u);
}

void modify(int u, int p, ll v) {
if(t[u].l == t[u].r) {
for(int i = 0;i <= 32; i++) {
t[u].OR[i] = v & 1;
v >>= 1;
}
return ;
}
if(p <= mid) modify(lc, p, v);
else modify(rc, p, v);
push_up(u);
}

vector<ll> query(int u, int ql, int qr) {
if(ql <= t[u].l && t[u].r <= qr) {
vector<ll> ans(33, 0);
for(int i = 0;i <= 32; i++) ans[i] = t[u].OR[i];
return ans;
}
vector<ll> ans(33, 0), temp1(33, 0), temp2(33, 0);
if(ql <= mid) temp1 = query(lc, ql, qr);
if(qr > mid) temp2 = query(rc, ql, qr);
for(int i = 0;i <= 32; i++) ans[i] = temp1[i] + temp2[i];
return ans;
}

void solve() {
bit[0] = 1;
for(int i = 1;i < N; i++) bit[i] = bit[i - 1] * 2 % mod;

int n; scanf("%d",&n);
for(int i = 1;i <= n; i++) scanf("%lld",&a[i]);
build(1, 1, n);
int m; scanf("%d",&m);
while(m--) {
int opt; scanf("%d",&opt);
if(opt == 1) {
int x; ll y; scanf("%d%lld",&x,&y);
modify(1, x, y);
}
else {
int l, r; scanf("%d%d",&l,&r);
vector<ll> ans = query(1, l, r);
ll res = 0;
for(int i = 0;i <= 32; i++) {
res = (res + bit[i] % mod * (bit[ans[i]] - 1 + mod) % mod) % mod;
}
printf("%lld\n",res);
}
}
}

signed main() {
solve();
}

本文作者:jujimeizuo
本文地址https://blog.jujimeizuo.cn/2021/04/11/nowcoder-practice4-c/
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0 协议。转载请注明出处!