[BZOJ2171] K凹凸序列

题意

一个序列的第1,3,5…项被称作奇数项,第2,4,6…项被称作偶数项。一个序列A[1..n]被称作ZigZag序列当且仅当以下两个条件中的一个(或两个)成立:
1)除了首项,所有的奇数项都比它的前项小且所有的偶数项都比它的前项大。
一个序列A[1..n]被称作K凹凸序列当且仅当它的最长ZigZag子序列(不一定是连续子序列)的长度不超过K。现在有一个序列A[1..n],每次可以花费1的代价使得A中的某一项增加或减少1。我们的目的是花费最少的代价让它成为K凹凸序列。输出最小代价。
前1个测试点满足:K=1,N≤20000
第2~8个测试点满足:K=2,N≤20000
第9~15个测试点满足:K=3,N≤20000
第16~20个测试点满足:K≤10,N≤1000
所有测试点满足:A[i]≤50000

题解

这题的题目数据很重要。
K=1,将A[i]都变为中位数。
K=2,与[BZOJ1367] [Baltic2004]sequence相同
K=3,序列将变为∧或 ∨两种形状,枚举转折点,按照K=2的算法计算代价
K>3时N<=1000,这告诉我们 O(n2K) O(n2lognK) 复杂度是可以的。一个K凹凸序列一定是由K-1个单调段组成,那么按K=2的方法预处理处将一段区间变为单调增或减的代价,就可以dp了。dp思想比较简单不再赘述。
这道题要注意编写技巧,不然要码很长代码。

代码

/// by ztx
/// blog.csdn.net/hzoi_ztx
#include <bits/stdc++.h>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v)  for(i=v.begin();i!=v.end();i++)
#define r(x)   read(x)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
    if (CH == '-') NEG = true , CH = getchar() ;
    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
    if (NEG) ret = -ret ;
}

#define  kN  200010LL
#define  infi  0x3f3f3f3fLL
int n, K, a[kN];
inline void work1() {
    std::sort(a+1,a+n+1);
    int mid = a[(n+1)/2], ans = 0, i;
    Rep (i,1,n) ans += std::abs(a[i]-mid);
    printf("%d\n", ans);
}
#define  ltdata  int
struct lt {
    lt *l, *r;
    int d, sz;
    ltdata w;
    lt();
    lt(const ltdata&w);
}*null=new lt();
lt::lt() { l=r=null;d=sz=0; }
lt::lt(const ltdata&w): w(w) { l=r=null;d=0;sz=1; }
lt* Init(const ltdata&x) { return new lt(x); }
lt* Merge(lt*u,lt*v) {
    if (u == NULL || u == null) return v; if (v == null) return u;
    if (u->w < v->w) std::swap(u,v);
    u->r = Merge(u->r,v);
    if (u->l->d < u->r->d) std::swap(u->l,u->r);
    u->d = u->r->d+1;
    u->sz = u->l->sz+u->r->sz+1;
    return u;
}
void Pop(lt*&u) { lt*tmp = u; u = Merge(u->l,u->r); delete tmp; }
int Size(lt*&u) { return u->sz; }
ltdata Top(lt*&u) { return u->w; }
int L[kN], cost[kN], sum[kN], hsum[kN];
lt*h[kN];
inline int work(int ret[kN],int ban=0) {
    int i, j, mid;
    ret[ban] = 0;
    Rep (i,ban+1,n) {
        L[i] = i, cost[i] = 0, sum[i] = hsum[i] = a[i], ret[i] = ret[i-1];
        h[i] = Init(a[i]);
        while (j=L[i]-1, j>ban && Top(h[i])<Top(h[j])) {
            ret[i] -= cost[j]+cost[i];
            h[i] = Merge(h[i],h[j]);
            sum[i] += sum[j], hsum[i] += hsum[j];
            L[i] = L[j];
            mid = Size(h[i])-(i-L[i]+2)/2;
            while (mid --> 0) hsum[i] -= Top(h[i]), Pop(h[i]);
            mid = Size(h[i]);
            cost[i] = sum[i]-2*hsum[i];
            if ((i-L[i]+1) & 1) cost[i] += Top(h[i]);
            ret[i] += cost[i];
        }
    }
    return ret[n];
}
int LU[kN], LD[kN], RU[kN], RD[kN];
inline void work2() {
    int ans1 = work(LU), ans2, i;
    Rep (i,1,n) a[i] = -a[i]; ans2 = work(LD);
    printf("%d\n", std::min(ans1,ans2));
}
inline void work3() {
    int i, ans = infi;
    work(LU);
    std::reverse(a+1,a+n+1); work(RU);
    Rep (i,1,n) a[i] = -a[i]; work(RD);
    std::reverse(a+1,a+n+1); work(LD);
    Rep (i,0,n)
        ans=std::min(std::min(ans,LU[i]+RU[n-i]),LD[i]+RD[n-i]);
    printf("%d\n",ans);
}
#undef kN
#define kN  1010LL
int U[kN][kN], D[kN][kN], fu[10][kN], fd[10][kN];
void work4() {
    int i, j, k, ans = infi;
    Rep (i,1,n) {
        work(LU,i-1);
        Rep (j,i,n) U[i][j] = LU[j];
        a[i] = -a[i];
    }
    Rep (i,1,n) {
        work(LD,i-1);
        Rep (j,i,n) D[i][j] = LD[j];
    }
    Rep (i,0,n) rep (k,0,K) fu[k][i] = fd[k][i] = infi;
    fu[0][0] = fd[0][0] = 0;
    Rep (i,1,n) rep (k,1,K) rep (j,0,i)
        fu[k][i] = std::min(fu[k][i],fd[k-1][j]+U[j+1][i]),
        fd[k][i] = std::min(fd[k][i],fu[k-1][j]+D[j+1][i]);
    rep (k,1,K) ans = std::min(std::min(ans,fu[k][n]),fd[k][n]);
    printf("%d\n", ans);
}
#undef kN

int main() {
    int i;
    r(n), r(K);
    Rep (i,1,n) r(a[i]);
    if (K == 1) { work1(); goto END; }
    if (K == 2) { work2(); goto END; }
    if (K == 3) { work3(); goto END; }
    work4();
    END: getchar(), getchar();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值