線段樹

lowbit發表於2024-11-25

P5670

prob:

1.區間加
2.區間異或和後 m 位 (\(m \le 10\))
3.n 1e5

sol:

用 bitset 維護線段樹區間。
具體的,開 1024 位 bitset 表示每種數對異或貢獻為 0/1,有區間可加性。
時間複雜度 \(O(\frac{n \log n 2^m}{w})\),但是這題半秒,很卡常,需要最佳化。。(感覺甚至不如暴力/lh)
發現區間較小時 bitset 算確實不如暴力優,令長度小於閾值的區間暴力算即可。

注意:
1.暴力算的時候要把當前節點的 tag 加上。
2.不要習慣性的把迴圈裡所有東西都寫成 i !!!(modify 裡那個 p)

namespace sgt {
	#define ls(p) p<<1
	#define rs(p) p<<1|1
	const int M = 1<<10;
	const int B = 1<<5; // bf range
	bitset <M> b[N<<2];
	int ad[N<<2];
	void pushup(int p) {
		b[p] = b[ls(p)]^b[rs(p)];
	}
	void upd(int p, int k) {
		k &= M-1;
		b[p] = b[p]<<k | b[p]>>M-k;
		ad[p] += k;
	}
	void pushdown(int p) {
		if(!ad[p]) return ;
		upd(ls(p), ad[p]), upd(rs(p), ad[p]);
		ad[p] = 0;
	}
	void build(int p, int l, int r) {
		if(r-l+1 <= B) {
			rep(i, l, r) b[p].flip(a[i]);
			return ;
		}
		int mid = l+r>>1;
		build(ls(p), l, mid), build(rs(p), mid+1, r);
		pushup(p);
	}
	void modify(int p, int l, int r, int x, int y, int k) {
		if(r-l+1 <= B) {
			int lb = max(l, x), rb = min(r, y);
			rep(i, lb, rb) b[p].flip(a[i]+ad[p] & M-1);
			rep(i, lb, rb) a[i] += k;
			rep(i, lb, rb) b[p].flip(a[i]+ad[p] & M-1);
			return ;
		}
		if(l >= x && r <= y) return upd(p, k);
		pushdown(p);
		int mid = l+r>>1;
		if(x <= mid) modify(ls(p), l, mid, x, y, k);
		if(y > mid) modify(rs(p), mid+1, r, x, y, k);
		pushup(p);
	}
	bitset <M> ask(int p, int l, int r, int x, int y) {
		bitset <M> s;
		if(r-l+1 <= B) {
			int lb = max(l, x), rb = min(r, y);
			rep(i, lb, rb) s.flip(a[i]+ad[p] & M-1);
			return s;
		}
		if(l >= x && r <= y) return b[p];
		pushdown(p);
		int mid = l+r>>1;
		if(x <= mid) s ^= ask(ls(p), l, mid, x, y);
		if(y > mid) s ^= ask(rs(p), mid+1, r, x, y);
		return s;
	}
}

總結:bitset 太神秘!

ABC356F

唐題