BOI2004 sequence

传送门

这道题是真的巧妙,像我这样的估计永远想不出来咋做了。

这道题要求的是严格递增,我们先考虑比较简单一点的,改成严格不下降的。

这样的话,对于原序列a,如果它自身某一段是不下降序列的话,那么我们直接让b与之相等即可,那如果是下降的序列呢?

我们可以说明,这段下降序列的答案一定是序列中所有数的中位数。我们先考虑只有两个数的情况,这个时候一定是中位数(其实只要是两个数中间任意一个值都行),之后我们把这个规律拓展,差不多就能明白答案一定是中位数了。(其实好像取到一个区间之内的值都行,但是中位数肯定是在里面的)

所以说,我们现在就知道了对于每一段区间,区间内答案相同的情况,那我们如何合并呢?

假设我们现在已经有一个答案序列b满足不递减,如果新插入的一项仍然满足不递减的话,那么咱就不用管了,如果递减的话,那我们就用刚才的操作,我们找到前一段答案相同的区间,把两者合并,新的答案变为区间中位数,直到满足答案不递减为止。

如何维护中位数?只要维护一个大根堆,一旦堆中元素个数超过了区间长度一半就开始把大元素全给删了,直到符合条件为止。

你可能和我一样有疑问,直接删除?那会不会在后来你删除的一个数成为了中位数?

并不会。

首先,一个被删除的数如果想成为中位数,那么后面必须有更大的数加入,但是如果是更大的数的话,我们根本不会进行合并,而如果加入的是更小的数的话,那么中位数肯定是在越变越小,那就更不会用到这个被删除的数了。(对于一段被固定的区间,中位数不会变大!)

或许你还有疑问,就是如果我先插入一个较大的数,然后再插入一个较小的数,两者合并之后,这样会不会影响到中位数?

答案还是不会,这个就是bin哥的理解了(%%%prophetB),如果你想要通过这种方法影响中位数的话,那么你必然是插入两个数,一个较小,一个较大,但是这样的话其实并不会对中位数的位置产生影响,所以该被删除的还是会被删除,没什么影响。用bin哥的原话说,就是因为每次只插入一个数,对中位数只有一个数的影响,所以不会改变什么。

所以我们就可以愉快的使用左偏树来维护这个合并和删除的操作啦!

哦,说了一大堆,我们这讨论的都是不递减的情况,题目要求递增,怎么办呢?这个很容易,我们把输入的每个数分别减去它的下标,之后我们只要照着不递减的情况做就可以了。

看一下代码。

// luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<queue>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
#define lowbit(x) x & (-x)

using namespace std;
typedef long long ll;
const int M = 2000005;
const int INF = 1000000009;

ll read()
{
    ll ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

ll n,a[M],ans,v[M];
int l[M],r[M],lc[M],rc[M],size[M],tot,cnt,dis[M],root[M];

void pushup(int x)
{
    size[x] = size[lc[x]] + size[rc[x]] + 1;
}

int merge(int x,int y)
{
    if(!x || !y) return x | y;
    if(v[x] < v[y]) swap(x,y);
    rc[x] = merge(rc[x],y);
    if(dis[rc[x]] > dis[lc[x]]) swap(lc[x],rc[x]);
    dis[x] = dis[rc[x]] + 1;
    pushup(x);
    return x;
}

int del(int x)
{
    return merge(lc[x],rc[x]);
}

int insert(int x)
{
    v[++tot] = x,size[tot] = 1;
    lc[tot] = rc[tot] = dis[tot] = 0;
    return tot;
}

int main()
{
    n = read();
    rep(i,1,n) a[i] = read(),a[i] -= i;
    rep(i,1,n)
    {
    root[++cnt] = insert(a[i]),l[cnt] = r[cnt] = i;
    while(cnt > 1 && v[root[cnt]] < v[root[cnt-1]])
    {
        cnt--,root[cnt] = merge(root[cnt],root[cnt+1]);
        r[cnt] = r[cnt+1];
        while((size[root[cnt]] << 1) >= r[cnt] - l[cnt] + 3) root[cnt] = del(root[cnt]);
    }
    }
    rep(i,1,cnt)
       rep(j,l[i],r[i]) ans += abs(v[root[i]] - a[j]);
    printf("%lld\n",ans);
    rep(i,1,cnt)
    rep(j,l[i],r[i]) printf("%lld ",v[root[i]] + j);
    return 0;
}

 

posted @ 2018-10-16 23:08  CaptainLi  阅读(189)  评论(0编辑  收藏  举报