【并查集 单调栈】最短路

题面

思路

先考虑求最短路。
如果一个点开始往前走了,那就不用往后走了,因为它一开始就可以往后走。
所以我们先处理往后走的,再处理往前走的。
往后:用一个单调栈维护单调递增的最短路(从前往后扫点,肯定是往后靠的且小的用来更新答案)
往前:在往后的步骤中预处理,用并查集标记更新过的点
每个点为起点进行操作,计算答案即可

代码

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>

std::vector<int> h[6001];
struct node {
	int dis, p;
}s[6001];
int n, tot, ans;
int a[6001], b[6001], f[6001], fa[6001];

int find(int x) {
	return fa[x] = fa[x] != x ? find(fa[x]) : x;
}

int main() {
	scanf("%d", &n);
	for (int i = 2; i <= n; i++)
		scanf("%d", &a[i]);
	a[1] = 1;
	for (int i = 2; i <= n; i++)
		scanf("%d", &b[i]);
	for (int i = 1; i <= n; i++) {
		tot = 0;
		memset(f, 127 / 3, sizeof(f));
		for (int j = 0; j <= n; j++) h[j].clear();
		f[i] = 0;
		s[++tot] = (node){0, i};
		h[0].push_back(i);
		for (int j = i + 1; j <= n; j++) {
			while (tot > 0 && s[tot].p >= b[j]) tot--;
			tot++;
			f[j] = s[tot].dis + 1;
			s[++tot] = (node){f[j], j};
			h[f[j]].push_back(j);
		}
		for (int j = 1; j <= n; j++)
			fa[j] = j;
		for (int ll = 0; ll <= n; ll++)
			for (int j = 0; j < h[ll].size(); j++)
				for (int xx = find(h[ll][j] - 1); xx >= a[h[ll][j]]; xx = find(xx - 1))//往后1个点跳
					if (f[xx] > ll + 1) {
						f[xx] = ll + 1;
						fa[xx] = xx - 1;
						h[f[xx]].push_back(xx);
					} else fa[xx] = xx - 1;
		for (int j = 1; j <= n; j++)
			ans ^= f[j] * (i + j);
	}
	printf("%d", ans);
}
posted @ 2020-08-10 21:43  nymph181  阅读(193)  评论(0)    收藏  举报