bzoj1367: [Baltic2004]sequence

来源:互联网 发布:诺依曼枕头 知乎 编辑:程序博客网 时间:2024/06/01 12:29

题解

首先我们看,对于一段区间[l,r],他们如果是递增的,那么最优解就是对于z[i]=t[i],i[l,r],如果是递减的话那么最优解就是中位数了,即z[i]=t[(l+r)/2],i[l,r]如果我们把递增的区间拆成一个点一个点的,现在的序列就相当于有很多段递减的区间构成辣,那么现在的问题就是有很多段连续的区间,然后每段的答案都为一个值。我们考虑如何合并两段区间的答案.
假设我们要合并区间[l,n],[n+1,r]这两段区间,他们的最优解分别是{u,u,u,u},{u,u,u,u}
假设合并后答案为

{b[l],b[l+1],b[l+2],b[n],b[n+1],b[n+2],b[n+3],b[r]}
那么明显的,b[n]<=u,b[n+1]>=u假如b[n]>u,那么我们可以将{b[l],b[l+1],b[l+2],b[n]}变为{u,u,u,u},显然更优,对于b[n+1]>=u同理.
然后结合绝对值的几何意义,我们可以解改为
{b[n],b[n],b[n],b[n],b[n+1],b[n+1],b[n+1],b[n+1]}
当b[n+1]>u时,b[n]显然应该=u,而当b[n]=u时,显然b[n+1]=u时原式最小。当u’<=b[n+1]<=u时,显然b[n]等于b[n+1]时原式最小,综上所述原式最小时b[n]一定等于b[n+1],当b[n]=b[n+1]=w时,原式最小(w为两段序列的中位数)。那么合并后的两段区间的解变为
{w,w,w,w,w,w,w,w}

然后现在一个很容易想到的做法是用平衡树来维护中位数,复杂度两个log.
然后我们发现,按照上面的合并方式的话,我们也可以把递减的序列拆成一个一个的,我们可以把问题转化成一个元素一个元素的加入,维护单调的一个当前的解集。然后我们看加入一个元素后两段区间A,B被合并的情况,很显然当加入这个元素前,AmidA<BmidB然后加入后AmidA>BmidB(这样他们才有可能被合并),这就说明BmidA一定大于max{x,BmidB1}BmidA一定是两段序列中的第midA+midB大的数,那么midA,midB的奇偶进行简单的讨论一下后,会发现如果我们直接维护的A序列前midA个数和B序列前midB个数的话,然后将这些数合并的话,新序列C的前midC 个数一定出现且仅出现在这里面。所以我们可以对每一个序列维护前mid个数就ok了

#include<cstdio>#include<cstring>#include<algorithm>const int N = 1e6 + 9;int n;void G (int &num) {    static char a; static bool fl;    for (a = getchar (), fl = false; a > '9' || a < '0'; a = getchar ()) if (a == '-') fl = true;    for (num = 0; a >= '0' && a <= '9'; a = getchar ()) num = (num << 3) + (num << 1) + a - '0';    if (fl) num = -num;}void IO () {    freopen ("1367.in", "r", stdin);    freopen ("1367.out", "w", stdout);}struct LT {    int data, dist, size;    LT *l, *r;    void set () { data = dist = size = 0; l = r = this; }    void update () { dist = r -> dist + 1; size = l -> size + r -> size + 1; }    LT () {}    LT (int x, LT *fl) : data (x), dist (1), size (1), l (fl), r (fl) {}}*Null, *root[N], pool[N], *pis = pool;LT* Merge (LT *A, LT *B) {    if (A == Null) return B;    if (B == Null) return A;    if (A -> data < B -> data) std :: swap (A, B);    A -> r = Merge (A -> r, B);    if (A -> l -> dist < A -> r -> dist) std :: swap (A -> l, A -> r);    A -> update ();    return A;}LT* newnode (int xxx) {    return new (pis++) LT (xxx, Null);}void Pop (LT *&x) {    x = Merge (x -> l, x -> r);}int t[N], x, top, c[N], w[N], l[N], r[N];void Solve () {    Null = new (pis++) LT ();    Null -> set ();    G (n);    for (int i = 1; i <= n; ++i) {        scanf ("%d", &x); x -= i; w[i] = x;        root[++top] = newnode (x);        l[top] = r[top] = i;        c[top] = 1;        while (top > 1 && root[top] -> data < root[top - 1] -> data) {            --top;            root[top] = Merge (root[top], root[top + 1]);            if ((c[top] & 1) && (c[top + 1] & 1)) Pop (root[top]);            r[top] = r[top + 1];            c[top] += c[top + 1];        }    }    long long ret = 0;    for (int i = 1; i <= top; ++i) {        for (int j = l[i]; j <= r[i]; ++j) {            ret += abs (w[j] - root[i] -> data);        }    }    printf ("%lld\n", ret);}int main () {//  IO ();    Solve ();    return 0;}
1 0
原创粉丝点击