P6587 超超的序列 加強
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;
}