• [bzoj1049][HAOI2006]数字序列


      这题数据水得。。。似乎连O(n^3)都能过>_<

      第二问一直想不出来。。。跑去看了iwtwiioi神犇的题解 http://www.cnblogs.com/iwtwiioi/p/4160945.html

      对于一开始读入的数列a[],令b[i]=a[i]-i(中间要给别的数留空)....补集转化的思想,n-(b数组的最长不下降子序列长度)就是第一问的答案。

      第二问的话。。。

      把整个b数组搞成不下降的就可以使整个原数列严格上升。

      令f[i]为以i 结尾的最长上升子序列长度,对于一段上升序列中相邻的节点j和i(j<i,f[j]+1==f[i],b[j]<=b[i]):

        b[j+1...i-1]显然要么<b[j],要么>b[i]。。

        要把b[j...i]搞成不下降的,最优的方法里肯定存在一个整数k,使b[j...k-1]都等于b[j],b[k...i]都等于b[i]。

        具体证明见各路题解T_T。。。。以前写过一道同样结论的题(bzoj 1592: [Usaco2008 Feb]Making the Grade 路面修整)

        假设一开始,我们把b[j...i-1]全部变成b[i](因为这样比较好算),设将b[j...i-1]变为b[i]的费用为sum。

        所以我们将b[j..k-1]都变成b[j],b[k...i]都变成b[i]的费用sum'=sum-(b[i]-b[j])*(y-x),(x,y分别是b[j+1...k-1]中大于和小于b[i]的数的个数)(小于b[i]的数肯定也小于b[j])

        最佳的决策点就是要使(y-x)最大。。。

        y-x的值可以用前缀和算出来。。。令pre[z]表示b[j+1...z]中,大于b[i]的数比小于b[i]的数多多少个。。

        因为一个i 有多个j 对应,所以上文的j 是离i 最远的一个。

        设j'为当前的值。

        我们倒序枚举j’,枚举过程中b[j' ]肯定递增,所以(b[i]-b[j' ])递减,所以我们只要一直记录(y-x)的最大值就行了。

        对于当前j' ,(y-x)的最大值为max{ pre[z] }-pre[j'],(j'<=z<i),而sum值可以边枚举边累加。。。

      懒得考虑边界条件的话,可以先在b数组最前面插入极小值,最后面插入极大值,保证算出来的值可以使整个数列严格上升。

      总的时间复杂度是O(n^2)

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdlib>
     5 #include<cmath>
     6 #define ll long long
     7 using namespace std;
     8 const int maxn=35233;
     9 const int inf=1002333333;
    10 struct zs{
    11     int too,pre;
    12 }e[maxn];int last[maxn],tot;
    13 int a[maxn],b[maxn],c[maxn],pre[maxn],f[maxn];
    14 int st[maxn],top;
    15 ll cost[maxn];
    16 int i,j,k,n,m;
    17  
    18 int ra,fh;char rx;
    19 inline int read(){
    20     rx=getchar(),ra=0,fh=1;
    21     while((rx<'0'||rx>'9')&&rx!='-')rx=getchar();
    22     if(rx=='-')rx=getchar(),fh=-1;
    23     while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh;
    24 }
    25 inline int get(int x){
    26     register int l=1,r=top+1,mid;
    27     while(l<r){
    28         mid=(l+r)>>1;
    29         if(st[mid]<=x)l=mid+1;else r=mid;
    30     }
    31     return l;
    32 }
    33 inline void insert(int v,int i){e[++tot].too=i,e[tot].pre=last[v],last[v]=tot;}
    34  
    35 int main(){
    36     register int i,j;
    37     n=read()+2,top=0,st[top+1]=inf;
    38     b[1]=-inf,b[n]=inf-n;
    39     for(i=2;i<n;i++)
    40         a[i]=read(),b[i]=a[i]-i;
    41     for(i=1;i<=n;i++){
    42         f[i]=get(b[i]),st[f[i]]=b[i];
    43         if(f[i]>top)top++,st[top+1]=inf;
    44         insert(f[i],i);
    45     }
    46      
    47     memset(cost,60,(n+1)<<3),cost[1]=0;
    48     for(i=2;i<=n;i++){
    49         int mnpos=i,mxpre=-inf;ll sum=0;
    50         for(j=last[f[i]-1];j;j=e[j].pre)
    51             if(e[j].too<i&&b[e[j].too]<=b[i])mnpos=e[j].too;
    52         for(j=mnpos+1,pre[mnpos]=0;j<i;j++)
    53             pre[j]=pre[j-1]+(b[j]<b[i]?1:-1);
    54         for(j=i-1;j>=mnpos;j--){
    55             if(pre[j]>mxpre)mxpre=pre[j];
    56             if(f[j]+1==f[i]&&b[j]<=b[i])
    57                 cost[i]=min(cost[i],cost[j]+sum-(ll)(b[i]-b[j])*(mxpre-pre[j]));
    58             sum+=abs(b[j]-b[i]);
    59         }
    60     }
    61     printf("%d
    ",n-top);
    62     printf("%lld
    ",cost[n]);
    63     return 0;
    64 }
    View Code

      一开始枚举的方向错了。。搞半天搞不出来(虽然似乎也是可以搞的?)。。。跑去膜拜iwtwiioi的代码。。结果调来调去最后完全一样了>_<

  • 相关阅读:
    AngularJS 学习 (一)Start
    div固定位置,不随滚动条滚动
    两个单例小程序
    java实现人脸识别V3版本开发
    flex>行为和特效 小强斋
    flex>导航 小强斋
    flex>定位和布局 小强斋
    flex>菜单设计 小强斋
    flex>其他交互设计 小强斋
    flex>菜单设计 小强斋
  • 原文地址:https://www.cnblogs.com/czllgzmzl/p/5186419.html
Copyright © 2020-2023  润新知