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\n", 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值