前鬼后鬼的守护

题目大意

给定序列x。
将xi修改成x’要付出|x’-xi|的代价。
求最小代价使得xi<=xi+1

维护函数

设DP,Fi(x)表示把第i个修改成x使得前i个递增的最小代价。
Fi(x)=|x-xi|+min(Fi-1(1~x))
Fi就是函数嘛,容易观察出还是许多一次函数组成的单调函数。
每次就是加上两段一次函数,那个取min是把斜率大于0的直线改为平板。splay维护即可。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=500000+10,maxd=1e9;
int father[maxn*2],tree[maxn*2][2],kk[maxn*2],adk[maxn*2],L[maxn*2],sta[maxn*2];
ll bb[maxn*2],adb[maxn*2];
int i,j,k,l,t,n,m,tot,root,top;
ll ans;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void mark(int x,int k,ll b){
    if (!x) return;
    kk[x]+=k;adk[x]+=k;
    bb[x]+=b;adb[x]+=b;
}
void clear(int x){
    if (adk[x]||adb[x]){
        mark(tree[x][0],adk[x],adb[x]);
        mark(tree[x][1],adk[x],adb[x]);
        adk[x]=adb[x]=0;
    }
}
void remove(int x,int y){
    top=0;
    while (father[x]!=y){
        sta[++top]=x;
        x=father[x];
    }
    while (top){
        clear(sta[top]);
        top--;
    }
}
int pd(int x){
    return tree[father[x]][1]==x;
}
void rotate(int x){
    int y=father[x],z=pd(x);
    father[x]=father[y];
    if (father[y]) tree[father[y]][pd(y)]=x;
    tree[y][z]=tree[x][1-z];
    if (tree[x][1-z]) father[tree[x][1-z]]=y;
    tree[x][1-z]=y;
    father[y]=x;
    if (y==root) root=x;
}
void splay(int x,int y){
    remove(x,y);
    while (father[x]!=y){
        if (father[father[x]]!=y)
            if (pd(x)==pd(father[x])) rotate(father[x]);else rotate(x);
        rotate(x);
    }
}
/*int find(int x,int y){
    if (!x) return 0;
    if (y>=L[x]){
        int t=find(tree[x][1],y);
        if (t) return t;else return x;
    }
    else return find(tree[x][0],y);
}*/
void changemin(){
    int j=0,k=root;
    ll l;
    while (1){
        if (!k) break;
        clear(k);
        if (kk[k]<=0) k=tree[k][1];
        else{
            j=k;
            k=tree[k][0];
        }
    }
    if (j){
        splay(j,0);
        L[++tot]=L[j];
        l=(ll)kk[j]*L[j]+bb[j];
        kk[tot]=0;
        bb[tot]=l;
        tree[tot][0]=tree[j][0];
        if (tree[j][0]) father[tree[j][0]]=tot;
        root=tot;
    }
}
int main(){
    freopen("chen.in","r",stdin);freopen("chen.out","w",stdout);
    n=read();
    root=tot=1;
    L[1]=1;
    fo(i,1,n){
        t=read();
        //k=find(root,t);
        j=0;k=root;
        while (k){
            if (L[k]<=t){
                j=k;
                k=tree[k][1];
            }
            else k=tree[k][0];
        }
        k=j;
        splay(k,0);
        mark(tree[k][0],-1,t);
        mark(tree[k][1],1,-t);
        if (L[k]==t){
            kk[k]++;
            bb[k]-=(ll)t;
        }
        else{
            L[++tot]=L[k];
            tree[tot][0]=tree[k][0];
            if (tree[k][0]) father[tree[k][0]]=tot;
            tree[k][0]=tot;
            father[tot]=k;
            L[k]=t;
            kk[tot]=kk[k]-1;
            bb[tot]=bb[k]+(ll)t;
            kk[k]++;
            bb[k]-=(ll)t;
        }
        changemin();
    }
    k=root;
    while (tree[k][1]){
        clear(k);
        k=tree[k][1];
    }
    ans=(ll)kk[k]*maxd+bb[k];
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值