线段树

强大好用的数据结构!

Top:线段树尽量保持一种写法,不要再写错了!!!11

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<<7; // 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

唐题

2 log 无脑暴力线段树

先离散化,维护区间中数的总个数和联通块个数,顺便记录左右端点合并即可。

查询时直接二分联通块左右端点(若查询区间联通块数量为 1 则合法),注意最终答案要减掉自己重复算的一次。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

#define pb push_back
#define pc putchar
#define sp pc(' ')
#define et pc('\n')
#define debug cerr<<"--ERROR--\n"
#define rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define per(i, a, b) for(int i = (a); i >= (b); --i)
#define mem(a, x) memset(a, x, sizeof(a))
#define in(a, n) rep(i, 1, n) a[i]=rd()
#define all(x) x.begin(), x.end()

inline ll rd(){ll x=0; int f=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-48; c=getchar();} return x*f;}
inline void wr(ll x){if(x<0) putchar('-'), x=-x; static int stk[35]; int tp=0; do stk[tp++]=x%10;while(x/=10); do pc(stk[--tp]^48);while(tp);}

//const ldb eps = 1e-9;
const ll INF = 0x3f3f3f3f3f3f3f3f;
//const int mo = 1e9+7;
const int N = 2e5+3;

int n, m, op[N], vis[N];
ll k, a[N], x[N];
struct node {
	ll l, r;
	int tot, cnt;
	// leftest number, rightest number, total number, count of intervals
};
node operator + (const node &a, const node &b) {
	if(!a.tot) return b;
	if(!b.tot) return a;
	ll l = min(a.l, b.l), r = max(a.r, b.r);
	int s = a.tot+b.tot, c = a.cnt+b.cnt - (max(a.l, b.l)-min(a.r, b.r) <= k);
	return {l, r, s, c};
}
namespace sgt {
	#define ls(p) p<<1
	#define rs(p) p<<1|1
	node t[N<<2];
	void pushup(int p) {
		t[p] = t[ls(p)]+t[rs(p)];
	}
	void modify(int p, int l, int r, int x, int k) {
		if(l == r) {
			t[p].l = t[p].r = a[x];
			t[p].tot += k, t[p].cnt += k;
			return ;
		}
		int mid = l+r>>1;
		if(x <= mid) modify(ls(p), l, mid, x, k);
		else modify(rs(p), mid+1, r, x, k);
		pushup(p);
	}
	node ask(int p, int l, int r, int x, int y) {
		if(l >= x && r <= y) return t[p];
		int mid = l+r>>1;
		if(y <= mid) return ask(ls(p), l, mid, x, y);
		if(x > mid) return ask(rs(p), mid+1, r, x, y);
		return ask(ls(p), l, mid, x, y) + ask(rs(p), mid+1, r, x, y);
	}
}
using sgt::modify;
using sgt::ask;

void clear() {  }
int findl(int x) {
	int l=1, r=x, s=1;
	node t;
	while(l <= r) {
		int mid = l+r>>1;
		if((t=ask(1, 1, n, mid, x)).cnt == 1) r = mid-1, s = t.tot;
		else l = mid+1;
	}
	return s;
}
int findr(int x) {
	int l=x, r=n, s=1;
	node t;
	while(l <= r) {
		int mid = l+r>>1;
		if((t=ask(1, 1, n, x, mid)).cnt == 1) l = mid+1, s = t.tot;
		else r = mid-1;
	}
	return s;
}
signed main() {
//  freopen(".in", "r", stdin);
//  freopen(".out", "w", stdout);

int TT = 1;
//TT = rd();
while(TT--)
{
	clear();
	cin >> m >> k;
	rep(i, 1, m) {
		op[i]=rd(), x[i]=a[i]=rd();
	}
	sort(a+1, a+m+1);
	n = unique(a+1, a+m+1) - a-1;
	mem(vis, -1);
	rep(i, 1, m) {
		x[i] = lower_bound(a+1, a+n+1, x[i]) - a;
		if(op[i] == 1) {
			vis[x[i]] = -vis[x[i]];
			modify(1, 1, n, x[i], vis[x[i]]);
		}
		else {
			wr(findr(x[i]) + findl(x[i]) - 1), et;
		}
	}
    et;
}
    return 0;
}

讲个笑话,把 findl 当成 1 可以过样例和前面的点,别问我怎么知道的。这次终于不是线段树写错了!!(AT random数据是真的水)

posted @ 2024-11-25 23:39  lowbit  阅读(41)  评论(0)    收藏  举报