[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",n-top);
62     printf("%lld\n",cost[n]);
63     return 0;
64 }
View Code

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

 

posted @ 2016-02-11 13:44  czllgzmzl  阅读(472)  评论(0编辑  收藏  举报