YBTOJ 4.3RMQ问题

A.数列区间

image
image

板子 详见P3865 ST表

点击查看代码
#include <bits/stdc++.h>
using namespace std;

int n, m;
const int N = 1e5 + 0721;
int a[N], rmq[N][21], Log[N];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);

    Log[0] = -1;
    for (int i = 1; i <= n; ++i) Log[i] = Log[i >> 1] + 1;

    for (int i = 1; i <= n; ++i) rmq[i][0] = a[i];

    for (int j = 1; j <= 20; ++j) {
        for (int i = 1; i + (1 << j) - 1 <= n; ++i)
            rmq[i][j] = max(rmq[i][j - 1], rmq[i + (1 << (j - 1))][j - 1]);
    }

    for (int i = 1; i <= m; ++i) {
        int l, r;
        scanf("%d%d", &l, &r);
        int x = r - l + 1;
        int k = Log[x];
        printf("%d\n", max(rmq[l][k], rmq[r - (1 << k) + 1][k]));
    }

    return 0;
}

B.静态区间

image
image

\(RMQ\) 的拓展应用
因为 \(RMQ\) 查询的时候实际上是查询两个中间有重叠区域的信息
所以实际上只要重叠不影响的信息就可以用 \(RMQ\) 查询

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 0721;
int rmq[N][21];
int a[N];
int Log[N];
int n, m;

int gcd(int x, int y) {
    if (x < y)
        swap(x, y);
    if (y == 0)
        return x;
    else
        return gcd(y, x % y);
}

inline int query(int l, int r) {
    int k = Log[r - l + 1];
    return gcd(rmq[l][k], rmq[r - (1 << k) + 1][k]);
}

int main() {
    scanf("%d%d", &n, &m);
    Log[0] = -1;
    for (int i = 1; i <= n; ++i) Log[i] = Log[i >> 1] + 1;

    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);

    for (int i = 1; i <= n; ++i) rmq[i][0] = a[i];

    for (int j = 1; j <= 20; ++j) {
        for (int i = 1; i + (1 << j) - 1 <= n; ++i)
            rmq[i][j] = gcd(rmq[i][j - 1], rmq[i + (1 << (j - 1))][j - 1]);
    }

    while (m--) {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", query(l, r));
    }

    return 0;
}

C.与众不同

image
image

刚看这题口胡了一些类似于主席树二分离线线段树二分之类的做法
结果后来发现因为询问区间是 \(\left[l, r\right]\) 但是不一定答案区间左端点就是 \(l\) 所以就假了
非常的怄火
那不行就换个思路吧 我们注意到完美区间是一个连续区间
并且当 \(\left[l, r\right]\) 不是一个完美区间时 \(\left[l, r + 1\right]\) 一定不是一个完美区间
这不就可以双指针嘛!
我们设 \(f[i]\) 表示以 \(i\) 结尾最长的完美序列长度 然后双指针维护
我们接着考虑如何处理答案
因为不一定以 \(i\) 结尾的最长完美序列 左端点一定在 \(\left[l, r\right]\) 这个询问区间里
但是因为完美序列中间的任意一段都是完美区间 所以我们可以直接把它断掉
进一步思考 因为我们是双指针维护 所以当 \(r\) 右移时 \(l\) 一定单调不减
那我们就可以考虑二分 \(\left[l, r\right]\) 区间内第一个最长完美序列左端点在这个区间里的点 \(x\)
在它前面的 显然都要在 \(l\) 处断掉 所以最长的就是 \(\left[l, x - 1\right]\)
对于 \(\left[x, r\right]\) 这段区间 它们的最长完美序列都在这段区间里 所以可以直接用 \(ST\) 表查询 \(f[i]\) 的最大值
然后两个取个 \(max\) 即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 0721;
const int s6 = 1e6;
int lst[N], f[N], pre[N], a[N];
int Log[N];
int rmq[N][21];
int loc[N * 10];
int n, m;

inline int query(int l, int r) {
	int k = Log[r - l + 1];
	return max(rmq[l][k], rmq[r - (1 << k) + 1][k]);
}

int main() {
//	freopen("1.txt", "r", stdin);
	
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	for (int i = 1; i <= n; ++i) {
		lst[i] = loc[a[i] + s6];
		loc[a[i] + s6] = i;
	}
	
	for (int i = 1; i <= n; ++i) {
		pre[i] = max(pre[i - 1], lst[i] + 1);
		f[i] = i - pre[i] + 1;
		rmq[i][0] = f[i];
	}
//	for (int i = 1; i <= n; ++i) cout<<i<<" "<<pre[i]<<endl;	

	
	Log[0] = -1;
	for (int i = 1; i <= n; ++i) Log[i] = Log[i >> 1] + 1;
	for (int j = 1; j <= 20; ++j) {
		for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
			rmq[i][j] = max(rmq[i][j - 1], rmq[i + (1 << (j - 1))][j - 1]);
//			cout<<i<<" "<<j<<" "<<rmq[i][j]<<endl;
		}
	}
	
	while (m--) {
		int l, r;
		scanf("%d%d", &l, &r);
		++l, ++r;
		int ll = l, rr = r;
		int mid, ans = l - 1;
		while (l <= r) {
			mid = (l + r) >> 1;
			if (pre[mid] < ll) {
				ans = mid;
				l = mid + 1;
			}
			else
				r = mid - 1;
		}
		int res = ans - ll + 1;
		if (ans < rr)
		res = max(query(ans + 1, rr), ans - ll + 1);
		printf("%d\n",res);
	}
	
	return 0;
}

D.矩阵最值

image
image

二维 \(ST\) 表 还是经典不看题解自己先想
还是考虑从一维转移到二维 就是先把每一行都按倍增预处理好
全整好之后再把处理好的每一行看成这一列的一个格 然后每一列按倍增预处理
查询的时候也是查询四个重叠矩形

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int N = 300;
int f[N][N][11][11];
int Log[N];
int n, m, K;

inline int query(int x1, int y1, int x2, int y2) {
	int k = Log[x2 - x1 + 1], h = Log[y2 - y1 + 1];
	int max1 = max(f[x1][y1][k][h], f[x2 - (1 << k) + 1][y1][k][h]);
	int max2 = max(f[x1][y2 - (1 << h) + 1][k][h], f[x2 - (1 << k) + 1][y2 - (1 << h) + 1][k][h]);
	return max(max1, max2);
}

int main() {
	scanf("%d%d%d", &n, &m, &K);
	Log[0] = -1;
	for (int i = 1; i <= max(n, m); ++i) Log[i] = Log[i >> 1] + 1;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) scanf("%d", &f[i][j][0][0]);
	}
	
	for (int k = 0; k <= Log[n]; ++k) {
		for (int h = 0; h <= Log[m]; ++h) {
			for (int i = 1; i + (1 << k) - 1 <= n; ++i) {
				for (int j = 1; j + (1 << h) - 1 <= m; ++j) {
					if (k == 0 && h == 0) continue;
				if (k != 0)
					f[i][j][k][h] = max(f[i][j][k - 1][h], f[i + (1 << (k - 1))][j][k - 1][h]);
				else
					f[i][j][k][h] = max(f[i][j][k][h - 1], f[i][j + (1 << (h - 1))][k][h - 1]);
				}
			}
		}
	}
	
	while (K--) {
		int x1, y1, x2, y2;
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		printf("%d\n",query(x1, y1, x2, y2));
	}	
	
	return 0;
}

E.接水问题

image
image

首先这个看上去就很可以二分答案

我们考虑二分这个水槽的长度 然后暴力枚举区间起点来 \(check\)

那么实际上第一滴水和最后一滴水的间隔时间就是区间 \(\max\) 减区间 \(\min\) 这个东西就可以用 ST 表来查

这题的区间长度竟然是 \(r - l\) 而不是 \(r - l + 1\) /fn

点击查看代码
#include <bits/stdc++.h>
using namespace std;

namespace steven24 {
	
const int N = 1e6 + 0721;
const int inf = 0x7fffffff;
int maxn[N][21], minn[N][21];
int b[N];
bool vis[N];
int n, k, d;

struct node {
	int x, h;
} a[N];

void init() {
	for (int i = 1; i <= k; ++i) minn[i][0] = inf;
	for (int i = 1; i <= n; ++i) {
		minn[a[i].x][0] = min(minn[a[i].x][0], a[i].h);
		maxn[a[i].x][0] = max(maxn[a[i].x][0], a[i].h);
	}
	
	for (int j = 1; j <= 20; ++j) {
		for (int i = 1; i + (1 << j) - 1 <= k; ++i) {
			maxn[i][j] = max(maxn[i][j - 1], maxn[i + (1 << (j - 1))][j - 1]);
			minn[i][j] = min(minn[i][j - 1], minn[i + (1 << (j - 1))][j - 1]);
		}
	}
}

int qmax(int l, int r) {
	int tmp = log2(r - l + 1);
	return max(maxn[l][tmp], maxn[r - (1 << tmp) + 1][tmp]);
}

int qmin(int l, int r) {
	int tmp = log2(r - l + 1);
	return min(minn[l][tmp], minn[r - (1 << tmp) + 1][tmp]);
}

bool check(int len) {
	int ret = 0;
	for (int i = 1; i + len <= k; ++i) {
		int r = i + len;
		ret = max(ret, qmax(i, r) - qmin(i, r));
	}
	return ret >= d;
}

int binary_search() {
	int l = 1, r = k;
	int mid, ans = -1;
	while (l <= r) {
		mid = (l + r) >> 1;
		if (check(mid)) {
			ans = mid;
			r = mid - 1;
		} else
			l = mid + 1;
	}
	return ans;
}

void main() {
	freopen("flowerplot.in", "r", stdin);
	freopen("flowerplot.out", "w", stdout);
	
	scanf("%d%d", &n, &d);
	for (int i = 1; i <= n; ++i) {
		scanf("%d%d", &a[i].x, &a[i].h);
		k = max(k, a[i].x);
	}
	init();
	printf("%d\n", binary_search());
}

}

int main() {
	steven24::main();
	return 0;
}
/*
4 5
6 3
2 4
4 10
12 15
*/


F.方阵问题

image
image

第一次做题被卡空间

一眼二维 ST 元素和直接二维差分秒了

一算空间 炸了

如果对每一行开 ST 然后询问的时候把每行都暴力查一遍 复杂度就是 \(4 \times 10 ^ 8\)

感谢 bot 帮忙卡常 直接暴力艹过去了(主要是把倍增数组开在最前面缩短寻址时间)

image

点击查看代码
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;

namespace steven24 {
	
const int N = 801;
const int inf = 0x7fffffff;
int maxn[10][N][N];
int minn[10][N][N];
int d[N][N];
char opt[4];
int n, m;

inline int read() {
    int xr = 0; 
	char cr;
    while (cr = getchar(), cr < '0' || cr > '9') ;
    while (cr >= '0' && cr <= '9') 
        xr = (xr << 3) + (xr << 1) + (cr ^ 48), cr = getchar();
    return xr;
}

void init() {
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) d[i][j] = d[i][j - 1] + maxn[0][i][j];
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) d[i][j] += d[i - 1][j];
	}
	
	for (int i = 1; i <= n; ++i) 
		for (int k = 1; k <= 9; ++k) 
			for (int j = 1; j + (1 << k) - 1 <= m; ++j) 
				maxn[k][i][j] = max(maxn[k - 1][i][j], maxn[k - 1][i][j + (1 << (k - 1))]),
				minn[k][i][j] = min(minn[k - 1][i][j], minn[k - 1][i][j + (1 << (k - 1))]);
}	

inline int query_sum(int x1, int y1, int x2, int y2) {
	return d[x2][y2] - d[x2][y1 - 1] - d[x1 - 1][y2] + d[x1 - 1][y1 - 1];
}

inline int query_max(int x, int l, int r) {
	int k = __lg(r - l + 1);
	return max(maxn[k][x][l], maxn[k][x][r - (1 << k) + 1]);
}

inline int query_min(int x, int l, int r) {
	int k = __lg(r - l + 1); 
	return min(minn[k][x][l], minn[k][x][r - (1 << k) + 1]);
}

void main() {
	n = read(), m = read();
	for (int i = 1; i <= n; ++i) 
		for (int j = 1; j <= m; ++j) 
			minn[0][i][j] = maxn[0][i][j] = read();
	init();
	
	int T = read();
	while (T--) {
		scanf("%s", opt);
		int x1, x2, y1, y2;
		x1 = read(), y1 = read(), x2 = read(), y2 = read();
		++x1, ++y1, ++x2, ++y2;
		if (opt[1] == 'U') printf("%d\n", query_sum(x1, y1, x2, y2));
		else if (opt[1] == 'A') {
			int ans = 0;
			for (int i = x1; i <= x2; ++i) ans = max(ans, query_max(i, y1, y2));
			printf("%d\n", ans);
		} else {
			int ans = inf;
			for (int i = x1; i <= x2; ++i) ans = min(ans, query_min(i, y1, y2));
			printf("%d\n", ans);
		}
	}
}

}

int main() {
	steven24::main();
	return 0;
}
/*
3 3
1 2 3
4 5 6
7 8 9
3
SUM 0 0 1 1
MAX 0 0 2 2
MIN 0 1 1 1
*/

G.降雨量

image
image

竟然能一次 AC 这题 感动

本质就是多情况分讨 看着就很不想写 这一章最后写的题 只能说没写 CSP-S2022 T2 来还债了

主要把思路捋清

首先根据题目分析 为方便起见 我们设 \(Z\) 表示 \(X + 1\)\(Y - 1\) 这段年份的降雨量最大值

我们可以得到两个信息:

  • \(X \le Y\)
  • \(Z < X\)

把这两个信息综合一下 我们还可以得到 \(Z < Y\)

这样我们就得到了这三个量之间两两的关系
知道这个关系可以让思路清晰不少

我们设 \(flag1/2/3\) 分别表示这三条关系的真假 它们的初值都是 \(-1\) 表示真假未知

  • 如果 \(X\)\(Y\) 的降水量都已知并且 \(X \le Y\)\(flag1 = 1\)

  • 如果 \(X\)\(Y\) 的降水量都已知并且 \(X > Y\)\(flag1 = 0\)

  • 如果 \(\left[X, Y\right]\) 这段的降水量都已知 \(X\) 的降水量已知 并且 \(X > Z\)\(flag2 = 1\)

  • 如果 \(X\) 的降水量已知 并且 \(X \le Z\)\(flag2 = 0\)

  • 如果 \(\left[X, Y\right]\) 这段的降水量都已知 \(Y\) 的降水量已知 并且 \(Y > Z\)\(flag3 = 1\)

  • 如果 \(Y\) 的降水量已知 并且 \(Y \le Z\)\(flag3 = 0\)

那么如果最后 \(flag1/2/3\) 全为 \(1\) 就是 true
如果 \(flag1/2/3\) 有一个为 \(0\) 就是 false
否则 就是 maybe

我们发现 \(Z\) 就是区间最大值 拿 ST 表维护即可
对于判断某段区间降水量是否已知 我们参考 ST 表 用 \(exist_{i, j}\) 表示 \(\left[i, i + 2^j - 1\right]\) 这段区间内的降水量是否都已知
那么转移的时候就是左右两半的与
并且 \(X\)\(Y\) 的降水量不一定已知 所以要用二分查找编号

点击查看代码
#include <bits/stdc++.h>
using namespace std;

namespace steven24 {
	
const int N = 5e4 + 0721;
int maxn[21][N];
bool exist[21][N];
int Log[N];
int yy[N];
int n, m;

struct node {
	int y, r;
} a[N];	

void init() {
	for (int i = 2; i <= n; ++i) Log[i] = Log[i >> 1] + 1;
	for (int i = 1; i <= n; ++i) maxn[0][i] = a[i].r;
	for (int j = 1; j <= Log[n]; ++j) {
		for (int i = 1; i + (1 << j) - 1 <= n; ++i) maxn[j][i] = max(maxn[j - 1][i], maxn[j - 1][i + (1 << (j - 1))]);
	}
	
	for (int i = 1; i < n; ++i) {
		if (a[i + 1].y == a[i].y + 1) exist[1][i] = 1;
		else exist[1][i] = 0;
	}
	for (int j = 2; j <= Log[n]; ++j) {
		for (int i = 1; i + (1 << j) - 1 <= n; ++i) exist[j][i] = (exist[j - 1][i] & exist[j - 1][i + (1 << (j - 1))]);
	}
}

int query_max(int l, int r) {
	if (l > r) return 0;
	int k = Log[r - l + 1];
	return max(maxn[k][l], maxn[k][r - (1 << k) + 1]);
}

bool query_exist(int l, int r) {
	int k = Log[r - l + 1];
	return (exist[k][l] & exist[k][r - (1 << k) + 1]);
}

void main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d%d", &a[i].y, &a[i].r);
		yy[i] = a[i].y;
	}
	init();
	scanf("%d", &m);
	while (m--) {
		int x, y;
		scanf("%d%d", &x, &y);
		int flag1 = -1, flag2 = -1, flag3 = -1;
		bool existz;
		int locx = lower_bound(yy + 1, yy + 1 + n, x) - yy - 1;
		int locy = lower_bound(yy + 1, yy + 1 + n, y) - yy;
		if (a[locx + 1].y == x) ++locx;
		
		if (a[locx].y == x && a[locy].y == y) existz = query_exist(locx, locy);
		else if (a[locx].y == x) existz = query_exist(locx, locy - 1);
		else if (a[locy].y == y) existz = query_exist(locx + 1, locy);
		else existz = query_exist(locx + 1, locy - 1);
		
		if (a[locx].y == x && a[locy].y == y && a[locx].r >= a[locy].r) flag1 = 1;
		else if (a[locx].y == x && a[locy].y == y && a[locx].r < a[locy].r) flag1 = 0;
		
		if (existz && a[locx].y == x && a[locx].r > query_max(locx + 1, locy - 1)) flag2 = 1;
		else if (a[locx].y == x && a[locx].r <= query_max(locx + 1, locy - 1)) flag2 = 0;
		
		if (existz && a[locy].y == y && a[locy].r > query_max(locx + 1, locy - 1)) flag3 = 1;
		else if (a[locy].y == y && a[locy].r <= query_max(locx + 1, locy - 1)) flag3 = 0;
		
		if (flag1 == 1 && flag2 == 1 && flag3 == 1) printf("true\n");
		else if (flag1 == 0 || flag2 == 0 || flag3 == 0) printf("false\n");
		else printf("maybe\n");
	}
}
	
}

int main() {
	steven24::main();
	return 0;
}
/*
6
2002 4920
2003 5901
2004 2832
2005 3890
2007 5609
2008 3024
5
2002 2005
2003 2005
2002 2007
2003 2007
2005 2008 
*/

H.超级钢琴

image
image

八个月了 怎么一点长进没有 玉玉了

首先把这个区间和转化成前缀和差分

然后考虑枚举选取区间的左端点 \(i\) 那么右端点就是在 \(\left[i + l - 1, i + r - 1\right]\) 这段区间内前缀和最大的点 这个东西可以用 ST 表查

然后把这玩意推进堆

每次弹出的时候就把它换成这段区间的次大值、次次大值...推进去
到这就不会了 以下是正解剩余部分内容

设当前区间前缀和最大的点的位置是 \(p\) 那么次大值要么是 \(\left[i + l - 1, p - 1\right]\) 的最大值要么是 \(\left[p + 1, i + r - 1\right]\) 的最大值 直接把两个都放进堆里就好了

更具体地 我们设三元组 \((i, l, r)\) 表示左端点在 \(i\) 右端点在 \(\left[i + l - 1, i + r - 1\right]\) 这段区间的最大子段和
我们在记录一下这个取到这个最大子段和对应的位置 假设这个位置为 \(i + p - 1\)

那么每次选取这段的时候就把 \((i, l, p - 1)\)\((i, p + 1, r)\) 再推进堆里即可

代码是八个月前写的 可能有些乱见谅

点击查看代码
#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int N = 500721;
int c[N], rmq[N][21], num[N][21];
int n, k, l, r;
ll ans;

struct node {
    int st, enl, enr, en, val;
    friend bool operator<(node x, node y) { return x.val < y.val; }
};

priority_queue<node, vector<node>, less<node> > q;

int qwz(int le, int ri) {
    int j = log2(ri - le + 1);
    if (rmq[le][j] < rmq[ri + 1 - (1 << j)][j])
        return num[ri + 1 - (1 << j)][j];
    else
        return num[le][j];
}

int main() {
    scanf("%d%d%d%d", &n, &k, &l, &r);
    for (int i = 1; i <= n; ++i) {
        int x;
        scanf("%d", &x);
        c[i] = c[i - 1] + x;
        rmq[i][0] = c[i];
        num[i][0] = i;
    }

    for (int j = 1; j <= 20; ++j) {
        for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
            if (rmq[i][j - 1] < rmq[i + (1 << (j - 1))][j - 1]) {
                rmq[i][j] = rmq[i + (1 << (j - 1))][j - 1];
                num[i][j] = num[i + (1 << (j - 1))][j - 1];
            } else {
                rmq[i][j] = rmq[i][j - 1];
                num[i][j] = num[i][j - 1];
            }
        }
    }

    for (int i = 1; i + l - 1 <= n; ++i) {
        int zuo = i + l - 1;
        int you = min(i + r - 1, n);
        q.push((node){ i, zuo, you, qwz(zuo, you), c[qwz(zuo, you)] - c[i - 1] });
        //		cout<<i<<" "<<zuo<<" "<<you<<" "<<qwz(zuo,you)<<" "<<c[qwz(zuo,you)]-c[i-1]<<endl;
    }

    while (k--) {
        int sta = q.top().st, lef = q.top().enl, righ = q.top().enr, wei = q.top().en, v = q.top().val;
        q.pop();
        ans += v;
        if (lef < wei)
            q.push((node){ sta, lef, wei - 1, qwz(lef, wei - 1), c[qwz(lef, wei - 1)] - c[sta - 1] });
        if (wei < righ)
            q.push((node){ sta, wei + 1, righ, qwz(wei + 1, righ), c[qwz(wei + 1, righ)] - c[sta - 1] });
    }

    printf("%lld", ans);

    return 0;
}

I.二叉查找树

image
image
image

没用到 ST 表(

很直接的一个想法就是根据它的代码暴力插入并且记录父亲

这样在随机数据下确实期望复杂度是 \(\text{O} (n \log n)\) 的 但是极端数据下会让整棵 BST 变成一条链 复杂度就退化到了 \(\text{O}(n^2)\) 不然也不会有平衡树这种东西

这里讲下我的思路

因为我们每次只需要找到当前这个点插入时应该连在哪个点上

而被连的点一定是左右儿子至少有一个为空的点

并且根据直觉 最后一层的能被连的点只会有一个

并且最后一层的每个点能管辖的点的区间应该是连续的

所以 \(\left[1, n\right]\) 这段区间应该是连续的一段一段被最后一层的不同点所管辖

于是想到开一个数组 其中 \(a_i\) 表示如果将值为 \(i\) 的点插当前的 BST 中 它的父亲节点就是 \(a_i\)

结合样例讲述一下具体过程

首先第一个数一定是根节点 此时任何数再插一定都要接到它下面 所以当前 \(a\) 数组就是

3 3 3 3 3 3 3 3

然后我们把 \(5\) 插入 那么此时 \(\left[4, 8\right]\) 的数插入一定就是接 \(5\)
对于 \(\left[1, 3\right]\) 的数 还是接 \(3\) (因为还有左儿子可以连)

3 3 3 5 5 5 5 5

然后插入 \(1\) \(1\) 要连 \(3\) 所有 \(\left[1, 2\right]\) 的数此时都要连 \(1\)

1 1 3 5 5 5 5 5

插入 \(6\) 接在 \(5\) 后面 所有 \(\left[6, 8\right]\) 的数此时都要连 \(6\)

1 1 3 5 5 6 6 6

持续此过程

1 1 3 5 5 6 8 8

1 1 3 5 5 6 7 8

1 2 3 5 5 6 7 8

1 2 3 4 5 6 7 8

至此 插入完成

我们发现 每次插入一个点 实际它管辖的区间就是:

  • 如果它 \(<\) 它父亲 就是它父亲原来管辖的左端点到它父亲 -1
  • 如果它 \(>\) 它父亲 就是它父亲 +1 到它父亲原来管辖的右端点

显然我们直接维护出每个点管辖的左右端点和该点的深度 那么维护 \(a\) 数组实际上就是一个区间赋值 直接用线段树维护即可 好像也可以用珂朵莉树(?

点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define ls (k << 1)
#define rs (k << 1 | 1)
#define mid ((l + r) >> 1)
using namespace std;

namespace steven24 {
	
const int N = 3e5 + 0721;
int a[N], dep[N];
int lv[N], rv[N];
int n;
ll ans;

struct segment_tree {

int lazy[N << 2];

inline void pushdown(int k) {
	lazy[ls] = lazy[k];
	lazy[rs] = lazy[k];
	lazy[k] = -1;
}

void modify(int k, int l, int r, int u, int v, int val) {
	if (u <= l && v >= r) {
		lazy[k] = val;
		return;
	}
	if (lazy[k] != -1) pushdown(k);
	if (u <= mid) modify(ls, l, mid, u, v, val);
	if (v > mid) modify(rs, mid + 1, r, u, v, val);
}	

int query(int k, int l, int r, int loc) {
	if (l == r) {
		return lazy[k];
	}
	if (lazy[k] != -1) pushdown(k);
	if (loc <= mid) return query(ls, l, mid, loc);
	else return query(rs, mid + 1, r, loc);
}

} seg;

inline int read() {
	int xr = 0, F = 1;
	char cr;
	while (cr = getchar(), cr < '0' || cr > '9') if (cr == '-') F = -1;
	while (cr >= '0' && cr <= '9')
		xr = (xr << 1) + (xr << 3) + (cr ^ 48), cr = getchar();
	return xr * F;
}

void main() {
	n = read();
	for (int i = 1; i <= n; ++i) a[i] = read();
	memset(seg.lazy, -1, sizeof seg.lazy);
	dep[a[1]] = 0;
	seg.modify(1, 1, n, 1, n, a[1]);
	lv[a[1]] = 1;
	rv[a[1]] = n;
	printf("%lld\n", ans);
	for (int i = 2; i <= n; ++i) {
		int val = seg.query(1, 1, n, a[i]);
		if (val == a[i]) continue;
		dep[a[i]] = dep[val] + 1;
		ans += dep[a[i]];
		printf("%lld\n", ans);
		if (val > a[i] && lv[val] <= val - 1) {
			seg.modify(1, 1, n, lv[val], val - 1, a[i]);
			lv[a[i]] = lv[val];
			rv[a[i]] = val - 1;
		}
		else if (val < a[i] && rv[val] >= val + 1) {
			seg.modify(1, 1, n, val + 1, rv[val], a[i]);
			lv[a[i]] = val + 1;
			rv[a[i]] = rv[val];
		}
	}
}
	
}

int main() {
	steven24::main();
	return 0;
}
/*
8
3
5
1
6
8
7
2
4
*/
posted @ 2023-06-30 09:03  Steven24  阅读(118)  评论(0编辑  收藏  举报