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
不错的一道考察乱搞能力的题目。