加载中...

8.13——962G(线段树查询区间min数量trick)

先讲重点:如何用线段树维护区间最小值以及数量?直接按照\(\{val,cnt\}\) 的方式将二者绑定在一起,并稍微改动下 \(pushup\)\(query\) 函数即可。具体细节见如下代码:

struct Min{
	int val, cnt;
};
 
struct Node{
	int l, r;
	Min o; // {val, cnt}: {最小值,最小值出现次数}
	int tag;
}tr[N << 2];
 
struct SegTree{
	#define lc p<<1
	#define rc p<<1|1
 
	void pushup(int p){
		if(tr[lc].o.val > tr[rc].o.val){
			tr[p].o = tr[rc].o;
		}
		else if(tr[lc].o.val < tr[rc].o.val){
			tr[p].o = tr[lc].o;
		}
		else{
			tr[p].o = {tr[lc].o.val, tr[lc].o.cnt + tr[rc].o.cnt};
		}
	}
 
	void pushdown(int p){
		if(tr[p].tag){
			tr[lc].tag += tr[p].tag;
			tr[rc].tag += tr[p].tag;
			tr[lc].o.val += tr[p].tag;
			tr[rc].o.val += tr[p].tag;
			tr[p].tag = 0;
		}
	}
 
	void build(int p, int l, int r){
		tr[p] = {l, r, Min{0, 1}, 0};
		if(l == r) return;
		int mid = l + r >> 1;
		build(lc, l, mid);
		build(rc, mid + 1, r);
		pushup(p);
	}
 
	void update(int p, int l, int r, int v){
		if(l <= tr[p].l && tr[p].r <= r){
			tr[p].o.val += v;
			tr[p].tag += v;
			return;
		}
		pushdown(p);
		int mid = tr[p].l + tr[p].r >> 1;
		if(l <= mid) update(lc, l, r, v);
		if(r > mid) update(rc, l, r, v);
		pushup(p);
	}
 
	Min query(int p, int l, int r){
		if(l <= tr[p].l && tr[p].r <= r){
			return tr[p].o;
		}
		pushdown(p);
		int mid = tr[p].l + tr[p].r >> 1;
		if(r <= mid) return query(lc, l, r);
		if(l > mid) return query(rc, l, r);
		Min lson = query(lc, l, r), rson = query(rc, l, r);
		Min t;
		if(lson.val < rson.val){
			t = lson;
		}
		else if(lson.val > rson.val){
			t = rson;
		}
		else{
			t = {lson.val, lson.cnt + rson.cnt};
		}
		return t;
	}
}seg;

G

容易想到破环为链。这样做相当于是固定删除了某一条边,好处在于:所有关系可以仅表示成一个区间的形式(若不破环则每种关系包括两个区间)。于是可以利用滑窗+线段树维护所有长度为 \(n\) 的子区间的覆盖情况:某条边只要未被任意一个关系表示的线段覆盖,这条边就可以被删除。于是问题转化成了查询区间内 \(0\) 的数量。由于 \(0\) 一定是区间最小值,因此等价于查询区间内最小值的数量,直接用上述板子即可。具体细节见代码。

code

posted @ 2025-08-13 12:45  jxs123  阅读(7)  评论(0)    收藏  举报