01trie + 树上维护
好题,使我调不出来。
观察 \(i\) 满足的条件,在二进制上分析,\(i\bmod 2^x\) 实际上就是从低位开始的前 \(x-1\) 位。那么所有满足条件的 \(i\) 从低位开始的前 \(x-1\) 位都相同,这类似相同的前缀。考虑建 01trie,那么所有满足条件的 \(i\) 构成第 \(x\) 层一个节点的子树,此时我们已经将下标限制转化为子树限制。
在树上类似动态开点线段树维护信息。需要实现区间加,单点查询,所以搞个 lazytag 啥的就做完了。
说着简单,细节很多。如:
- 信息合并可以记录路径,方便回溯,简单得多,要记得 top 清零!!!
- 结构体实现,函数写法应和普通线段树写法相似,不然调死你。
- 开 longlong。
复杂度 \(O(n\log n)\)。
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back
using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
int n, m, tot;
i64 a[N];
i64 op, x, y, v;
i64 lstans;
struct SEG {
int d[2];
i64 v, lzy, sz;
} t[N * 44];
void insert(int u, int dep, int i) {
if(dep > 20) return;
t[u].v += a[i], t[u].sz++;
int v = t[u].d[(i >> dep) & 1];
if(!v) {
t[u].d[(i >> dep) & 1] = ++tot;
v = tot;
}
insert(v, dep + 1, i);
}
int st[N * 44], top;
void upd(int u, int dep) {
st[++top] = u;
if(dep == x) {
while(top) t[st[top--]].v += 1LL * t[u].sz * v;
t[u].lzy += v;
return;
}
int v = t[u].d[(y >> dep) & 1];
if(!v) return;
upd(v, dep + 1);
}
void pd(int u, i64 v) {
t[u].v += 1LL * t[u].sz * v;
t[u].lzy += v;
}
i64 qry(int u, int dep) {
if(dep == x) return t[u].v;
int v = t[u].d[(y >> dep) & 1];
if(!v) return 0;
if(t[u].lzy) {
if(t[u].d[0]) pd(t[u].d[0], t[u].lzy);
if(t[u].d[1]) pd(t[u].d[1], t[u].lzy);
t[u].lzy = 0;
}
return qry(v, dep + 1);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
for(int i = 1; i <= n; i++) {
std::cin >> a[i];
insert(0, 0, i);
}
while(m--) {
std::cin >> op >> x >> y;
op = (lstans + op) % 2 + 1;
if(op == 1) {
std::cin >> v;
top = 0;
upd(0, 0);
} else {
std::cout << (lstans = qry(0, 0)) << "\n";
}
}
return 0;
}
标签:lzy,dep,return,超超,i64,int,std,P6587,序列
From: https://www.cnblogs.com/FireRaku/p/18277057