LuoGuP4331:[BOI2004]Sequence 数字序列

Pre

前前后后调试了四个小时

Solution

考虑当前一段序列与前面一段序列的中位数,递增则入栈,否则合并后取中位数再入栈。

正确性不会证明。

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 1000000 + 5;
int n;
inline int abs(int x){return x<0?-x:x;}
struct Lef {
	int fa[N], ch[N][2], dis[N], val[N], sz[N];
	inline int Find (int x) {
		int pre = x;
		while (fa[pre] != pre) {pre = fa[pre];}
		while (x != pre) {
			int u = fa[x];
			fa[x] = pre;
			x = u;
		}
		return pre;
	}
	inline int merge (int x, int y) {
		if (!x || !y) {return x + y;}
		if (x == y) {return x;}
		if (val[x] < val[y]) {swap (x, y);}
		ch[x][1] = merge (ch[x][1], y);
		fa[ch[x][1]] = x;
		sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
		if (dis[ch[x][1]] > dis[ch[x][0]]) {swap (ch[x][1], ch[x][0]);}
		dis[x] = dis[ch[x][1]] + 1;
		return x;
	}
	inline int del (int x) {
		int y = merge (ch[x][0], ch[x][1]);
		fa[y] = y;
		fa[x] = y;
		ch[x][0] = ch[x][1] = sz[x] = 0;
		return y;
	}
}tree;
int mip[N], top, id[N], L[N], stk[N], ans[N];
inline void solve () {
	for (int i = 1; i <= n; ++i) {
		id[i] = i;
		int tmp = i;
		while (top && mip[top] > tree.val[tree.Find(id[i])]) {
			tree.merge(tree.Find(id[stk[top]]), tree.Find(id[i]));
			tmp = L[top];
			int sz = (i - L[top] + 1 + 1) / 2;
			int ttmp = id[i];
			while (tree.sz[tree.Find(ttmp)] > sz) {
				ttmp = tree.del(tree.Find(ttmp));
			}
			id[i] = ttmp;
			top--;
		}
		top++;
		mip[top] = tree.val[tree.Find(id[i])];
		stk[top] = i;
		L[top] = tmp;
	}
	long long tot = 0;
	int now = 1;
	for (int i = 1; i <= n; ++i) {
		while (now != top && i >= L[now + 1]) {
			now++;
		}
		ans[i] = mip[now];
		tot += abs (mip[now] - tree.val[i]);
	}
	printf ("%lld
", tot);
	for (int i = 1; i <= n; ++i) {
		printf ("%d ", ans[i] + i);
	}
}
int main () {
	scanf ("%d", &n);
	for (int i = 1; i <= n; ++i) {tree.fa[i] = i; tree.sz[i] = 1;}
	for (int i = 1; i <= n; ++i) {scanf ("%d", &tree.val[i]); tree.val[i] -= i;}
	solve ();
	return 0;
}

Conclusion

不错的一道考察乱搞能力的题目。

原文地址:https://www.cnblogs.com/ChiTongZ/p/11248851.html