zoukankan      html  css  js  c++  java
  • [luogu4331]数字序列

    令$a'_{i}=a_{i}+n-i$、$b'_{i}=b_{i}+n-i$,代价仍然是$sum_{i=1}^{n}|a'_{i}-b'_{i}|$,但条件变为了$b'_{i}le b'_{i+1}$,即不下降(以下为了方便,$a'_{i}$和$b'_{i}$仍然用$a_{i}$和$b_{i}$表示,原来的不需要考虑)

    考虑暴力dp,令$f_{i,j}$表示前$i$个数且$b_{i}=j$的最小的代价,转移时先令$f_{i-1,j}=min_{kle j}f_{i-1,k}$,再加上绝对值,即$f_{i,j}=f_{i-1,j}+|a_{i}-j|$

    由于过程是交替进行的,可以看作先加上绝对值再取min(体现在定义上就是$f_{i,j}$表示前$i$个数且$b_{i}le j$的最小的代价),细节上由于第1次全部都是0本身就不需要取min,然后最后答案即为$f_{n,infty}$

    归纳$f_{i}$具有以下性质:其斜率单调不递增且小于等于0(即下凸但没有上升的部分)

    在这一条件下,对于取绝对值再取min的过程,可以看作:对于$a_{i}$左半部分相当于斜率增加1,对于$a_{i}$右半部分斜率减少1,特别的,由于要取min,因此对于斜率为0的部分仍然不变

    (做法上有一点类似[AGC049E](https://www.cnblogs.com/PYWBKTDA/p/14015313.html))

    定义$f'_{i,j}=f_{i,j}-f_{i,j-1}$($jge 1$),特别的$f'_{i,0}=sum_{j=1}^{i}a_{i}$,那么$f_{i,j}=sum_{k=0}^{j}f'_{i,k}$(其实后面两点不重要,因为求答案肯定通过构造出来的方案求更方便QAQ)

    然后对于$f'_{i,j}$($1le jle a_{i}$)区间减1,对于$f'_{i,j}$($a_{i}<j$且$f'_{i,j}<0$)区间加1,直接用线段树维护即可

    关于最优解的构造:先有$b_{n}=min_{f'_{n,k}=0}k$,然后再令$b_{i}=min(b_{i+1},min_{f'_{i,k}=0}k-1)$即可(很明显$b_{i+1}$之前$f_{i,j}$最小的位置就是这里,同时这里的$f_{i,j}$不会是跟$f_{i,j-1}$取min的结果)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 1000005
     4 #define L (k<<1)
     5 #define R (L+1)
     6 #define mid (l+r>>1) 
     7 map<int,int>mat;
     8 map<int,int>::iterator it;
     9 int n,m,a[N],b[N],val[N],f[N<<2],tag[N<<2];
    10 long long ans;
    11 char ch[21];
    12 int read(){
    13     int x=0;
    14     char c=getchar();
    15     while ((c<'0')||(c>'9'))c=getchar();
    16     while (('0'<=c)&&(c<='9')){
    17         x=x*10+c-'0';
    18         c=getchar();
    19     }
    20     return x;
    21 }
    22 void write(int k){
    23     int s=0;
    24     while (k){
    25         ch[++s]=k%10+'0';
    26         k/=10;
    27     }
    28     while (s)putchar(ch[s--]);
    29     putchar(' ');
    30 }
    31 void upd(int k,int x){
    32     tag[k]+=x;
    33     f[k]+=x;
    34 }
    35 void down(int k){
    36     upd(L,tag[k]);
    37     upd(R,tag[k]);
    38     tag[k]=0;
    39 }
    40 void update(int k,int l,int r,int x,int y,int z){
    41     if ((l>y)||(x>r))return;
    42     if ((x<=l)&&(r<=y)){
    43         upd(k,z);
    44         return;
    45     }
    46     down(k);
    47     update(L,l,mid,x,y,z);
    48     update(R,mid+1,r,x,y,z);
    49     f[k]=min(f[L],f[R]);
    50 }
    51 int query(int k,int l,int r){
    52     if (l==r)return l;
    53     down(k);
    54     if (f[R])return query(R,mid+1,r);
    55     return query(L,l,mid);
    56 }
    57 int main(){
    58     scanf("%d",&n);
    59     for(int i=1;i<=n;i++)a[i]=read()+n-i;
    60     memcpy(val,a,sizeof(val));
    61     sort(val+1,val+n+1);
    62     m=unique(val+1,val+n+1)-val-1;
    63     for(int i=1;i<=n;i++)a[i]=lower_bound(val+1,val+m+1,a[i])-val;
    64     for(int i=1;i<=n;i++){
    65         update(1,1,m,1,a[i],-1);
    66         if (i>1)update(1,1,m,a[i]+1,query(1,1,m),1);
    67         b[i]=query(1,1,m);
    68     }
    69     for(int i=n-1;i;i--)b[i]=min(b[i],b[i+1]);
    70     for(int i=1;i<=n;i++)ans+=abs(val[a[i]]-val[b[i]]);
    71     printf("%lld
    ",ans);
    72     for(int i=1;i<=n;i++)write(val[b[i]]-n+i);
    73 }
    View Code
  • 相关阅读:
    编译安装linux内核步骤
    怎样在github上协同开发
    在JSP里使用CKEditor和CKFinder
    tomcat结合nginx使用小结
    JAVA学习笔记——(五)
    JAVA学习笔记——(四)
    JAVA学习笔记——(三)
    JAVA学习笔记——(二)
    JAVA学习笔记——(一)
    ansible
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14180506.html
Copyright © 2011-2022 走看看