线段树杂题选胡

线段树杂题选胡

无特殊情况默认 a 为序列,n 为序列长度,m 是操作数。

无特殊情况默认 n,m105

T1

区间乘 1,询问区间最大子段和。

线段树维护区间最大/小的前缀/后缀/最大字段,乘 1 的时候交换一下即可。

T2

楼房重建,单点修改,区间询问有多少个位置上的数是前缀最大(或者可以理解为前缀单调栈大小)。

考虑楼房重建的那个pushup的方式也可以合并两个大小不相等的区间。

那么区间询问把询问到的这 log 个节点提出来pushup到一起即可知道答案。

T3

有一个序列 a,每个点为 (c,x,y),代表 (x,y) 有一个颜色为 c 的点。

支持单点修改和区间询问颜色普通且曼哈顿距离最大一对点的距离。

|x1x2|+|y1y2| 每个绝对值都有四种可能性,所以等价于四种情况取个 max

举例 x1x2+y1y2=(x1+y1)(x2+y2),其他的维护方法类似。

维护区间 wi=xi+yi 的最大值/次大值/最小值/次小值及其颜色,保证最大值和次大值以及最小值和次小值颜色不一样,正常线段树做就行,合并的时候分类讨论一下。

T4

单点修改,区间询问最小不能组成的数,计算复杂度默认 an 同阶,仅为常数上差距。

原题:19 ICPC 徐州 H

考虑全局询问贪心的过程,是从小到大排序,维护一个 s 代表 [0,s] 中的值都能表示出来,每次扫到一个 ai,那么 [0,s] 每个数加上 ai 就是 [ai,s+ai],如果 ais+1,则这两个区间就能拼起来,所以 ais+1 的时候 ss+ai,否则的话 s+1 这个值就不能被表示出来了,直接返回答案。

进而,可以维护一个权值线段树,每次查询权值在 [0,s+1] 中所有数的总和,如果没有数则返回 s+1,否则让 s 加上这些和,这个过程和上面的排序贪心是等价的。

发现如果不会返回 s+1 的话,那么每次 s 一定能加上 pre+1 的值,其中 pre 是上次的 s,这样子过程就是 loga 的。

这样区间询问直接主席树,带修外面套个树状数组。复杂度是三个log。

两个log做法:值域分成log段,每段是 [2i,2i+1),对于每个段,以原序列为下标,只考虑值域在这个段里面的数建一棵线段树,查询是每个段都会有 O(logn) 次查询,一次查询复杂度是 O(logn),修改只在对应值域段的线段树修改即可,所以查询复杂度单次 O(log2n),修改是 O(logn),时间复杂度 O(nloga+mlognloga)

T7

题意:求最长区间使得加入 k 个数后 sort 后最后为一个公差为 d 的等差数列。多解输出 l 最小的答案。

n,k2×105,0d109

  • d=0 的情况特判一下,找最长的值都相同的段即可

  • d0 的情况:

是公差为 d 等差数列需要满足两个条件:

  • mod d 相同;
  • 值两两不同。

可以单独处理每个极长的同余的段,然后分开处理:

先都整除一下 d,问题都转化成处理公差 d1 的情况。

这个区间合法要求 maxmin+1(RL+1)k,枚举 r 统计最小的 l 使得:

  • [L,R] 内元素无重复;

  • maxmin+1(RL+1)k

其中 max/min 为区间 [L,R] 的最大/小值

无重复的条件很好做,记录一下每个值上次出现的位置,然后记录一下 l 的下界 T

wL=max(L,R)min(L,R)+L

[T+1,R] 中最左的 L 使得 wLk+R,假如我们可以快速维护 w ,就可以解决这个问题。

怎样维护 w

注意 max(L,R) 是递减的,min 也类似。max(L,R) 可以维护一个单调栈,栈中的每个点都代表一段区间,它们的 max(L,R) 是这个栈中的这个元素。那么每次弹出的时候修改这个区间的 w 即可。

操作是区间加减,需要支持的查询为区间最左侧小于等于某个数的位置,这是个经典问题,记录一下区间最小值即可。

均摊下来复杂度是对的,操作数仅有 O(n) 个。

实现上有几个小细节,对于每个同余段重新建一棵大小为这个段长度的树,如果每次都 O(n) 建树,复杂度是错误的。ai 有负数,提前全加上 109 转化成非负数,不影响答案。

综上所述,我们完美的解决了这个问题,时间复杂度 O(nlogn)

//Code by do_while_true
#include<iostream>
#include<cstdio>
#include<map>
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T &read(T &r) {
	r = 0; bool w = 0; char ch = getchar();
	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
	while(ch >= '0' && ch <= '9') r = (r << 3) + (r <<1) + (ch ^ 48), ch = getchar();
	return r = w ? -r : r;
}
const int N = 200010;
const int INF = 0x7fffffff;
int n, k, d;
int a[N];
std::map<int, int>pre; 
#define tl tree[x].l
#define tr tree[x].r
#define ls tree[x].lson
#define rs tree[x].rson
int trnt;
struct SGT {
	int l, r, sum, mn, tag, lson, rson;
}tree[N << 1];
inline void pushup(int x) { tree[x].mn = Min(tree[ls].mn, tree[rs].mn); }
inline void pushdown(int x) {
	if(tree[x].tag) {
		int &p = tree[x].tag;
		tree[ls].mn += p; tree[rs].mn += p;
		tree[ls].tag += p; tree[rs].tag += p;
		p = 0; 
	}
}
int build(int l, int r) {
	int x = ++trnt; tl = l; tr = r;
	tree[x].mn = tree[x].tag = tree[x].lson = tree[x].rson = 0;
	if(l == r) return x;
	ls = build(l, (l + r) >> 1); rs = build(tree[ls].r+1, r);
	return x;
}
void modify(int x, int l, int r, int v) {
	if(tree[x].l >= l && tree[x].r <= r) {
		tree[x].mn += v;
		tree[x].tag += v;
		return ;
	}
	pushdown(x);
	int mid = (tr + tl) >> 1;
	if(mid >= l) modify(ls, l, r, v);
	if(mid < r) modify(rs, l, r, v);
	pushup(x); 
}
int query(int x, int l, int r, int v) {
	if(tree[x].mn > v) return INF;
	if(tl >= l && tr <= r) {
		if(tl == tr) return tl;
		pushdown(x); int sumq = 0;
		if(tree[ls].mn <= v) sumq = query(ls, l, r, v);
		else sumq = query(rs, l, r, v);
		pushup(x);
		return sumq;
	}
	pushdown(x);
	int mid = (tl + tr) >> 1, sumq;
	if(r <= mid) sumq = query(ls, l, r, v);
	else if(l > mid) sumq = query(rs, l, r, v);
	else {
		sumq = query(ls, l, r, v);
		if(sumq == INF) sumq = query(rs, l, r, v);
	}
	pushup(x);
	return sumq;
}
#undef tl
#undef tr
#undef ls
#undef rs
int L[N], R[N], bcnt, ansl = 1, ansr = 1;
int st1p[N], st1x[N], top1;
int st2p[N], st2x[N], top2;
int down;
signed main() {
//	freopen("in.txt", "r", stdin);
	read(n); read(k); read(d);
	if(!d) {
		for(int i = 1; i <= n; ++i) read(a[i]);
		int l = 1;
		for(int i = 2; i <= n; ++i) {
			if(a[i] != a[i-1]) {
				if(i - l > ansr - ansl + 1) ansl = l, ansr = i-1;
				l = i;
			}
		}
		printf("%d %d\n", ansl, ansr);
		return 0;
	}
	for(int i = 1; i <= n; ++i) read(a[i]), a[i] += 1000000000;
	L[bcnt = 1] = 1;
	for(int i = 2; i <= n; ++i) {
		if(a[i] % d != a[i-1] % d) {
			R[bcnt] = i-1;
			L[++bcnt] = i;
		}
	}
	R[bcnt] = n;
	for(int i = 1; i <= bcnt; ++i) {
		if(R[i] - L[i] == 0) continue ;
		int nowl = 1, nowr = 0;
		pre.clear();
		top1 = top2 = 0; trnt = 0; down = 1;
		build(1, R[i] - L[i] + 1);
		for(int j = L[i]; j <= R[i]; ++j) a[j] /= d;
		for(int j = L[i]; j <= R[i]; ++j) {
			if(pre[a[j]]) down = Max(down, pre[a[j]]-L[i]+2);
			pre[a[j]] = j;
			while(top1 && st1x[top1] <= a[j]) modify(1, st1p[top1-1]+1, st1p[top1], -st1x[top1]), --top1;
			while(top2 && st2x[top2] >= a[j]) modify(1, st2p[top2-1]+1, st2p[top2], st2x[top2]), --top2;
			st1x[++top1] = a[j]; st1p[top1] = j-L[i]+1;
			st2x[++top2] = a[j]; st2p[top2] = j-L[i]+1;
			modify(1, st1p[top1-1]+1, st1p[top1], st1x[top1]);
			modify(1, st2p[top2-1]+1, st2p[top2], -st2x[top2]);
			modify(1, j-L[i]+1, j-L[i]+1, j);
			int pl = query(1, down, j-L[i]+1, k+j);
			if(pl != INF && j - (pl+L[i]-1) + 1 > nowr - nowl + 1) nowl = pl+L[i]-1, nowr = j;
		}
		if(nowr - nowl + 1 > ansr - ansl + 1) ansl = nowl, ansr = nowr; 
	}
	printf("%d %d\n", ansl, ansr);
	return 0;
} 

T8

多组询问序列区间相差最小的两个数的差。

n,m2×105

暴力可以莫队然后搞个set,复杂度 O(n1.5logn)

枚举 R,维护左端点对应的答案。

To be continued

T9

给定 n,定义 work(x,y) 等于在 [1,n] 的线段树上队 [x,y] 进行区间询问后访问到的点的个数。

给定 L,R ,求 work(i,j) 的和,其中 LijR

线段树代表 [L,R] 的节点被 work(x,y) 统计到的重要条件是:

  • [L,R],[x,y] 有交

  • [faL,faR] 不被 [x,y] 包含

推一下式子发现是个二次函数,这样可以 O(Qn) 做,但是可以不用遍历所有的线段树的节点,直接区间查询,然后如果当前区间被完全包含于,子树是可以一起算的,提前把那个二次函数做个子树和即可。这样复杂度就是 O(Qlogn) 的了。

posted @   do_while_true  阅读(62)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?

This blog has running: 1845 days 1 hours 33 minutes 48 seconds

点击右上角即可分享
微信分享提示