P6587 超超的序列 加強

Fire_Raku發表於2024-06-30

P6587 超超的序列 加強

01trie + 樹上維護

好題,使我調不出來。

觀察 \(i\) 滿足的條件,在二進位制上分析,\(i\bmod 2^x\) 實際上就是從低位開始的前 \(x-1\) 位。那麼所有滿足條件的 \(i\) 從低位開始的前 \(x-1\) 位都相同,這類似相同的字首。考慮建 01trie,那麼所有滿足條件的 \(i\) 構成第 \(x\) 層一個節點的子樹,此時我們已經將下標限制轉化為子樹限制

在樹上類似動態開點線段樹維護資訊。需要實現區間加,單點查詢,所以搞個 lazytag 啥的就做完了。

說著簡單,細節很多。如:

  1. 資訊合併可以記錄路徑,方便回溯,簡單得多,要記得 top 清零!!!
  2. 結構體實現,函式寫法應和普通線段樹寫法相似,不然調死你。
  3. 開 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;
}

相關文章