bzoj 1049 [HAOI2006]数字序列

【bzoj1049】[HAOI2006]数字序列

Description

现在我们有一个长度为n的整数序列A。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。但是不希望改变过多的数,也不希望改变的幅度太大。

Input

第一行包含一个数n,接下来n个整数按顺序描述每一项的键值。

Output

第一行一个整数表示最少需要改变多少个数。 第二行一个整数,表示在改变的数最少的情况下,每个数改变的绝对值之和的最小值。

Sample Input

4
5 2 3 5

Sample Output

1
4

HINT

【数据范围】

90%的数据n<=6000。

100%的数据n<=35000。

保证所有数列是随机的。

 

这是证明:

 http://pan.baidu.com/share/link?uk=2651016602&shareid=1490516411

题解:

    这道题目第一问是十分简单,但是我wrong了好久。

    第二问是变化幅度。

    对于第一问,因为a[i]-a[j]>=i-j

    所以(a[i]-i)-(a[j]-j)>=0

    所以可以设b[i]=a[i]-i

    所以只需要b[i]-b[j]>=0即可

    fz[i]表示当前这个点到i这个点为结尾的最大长度。

    g[i]表示1-i之间的答案。

    g[i]=min g[j]+w[j+1,i],转移条件是fz[j]+1=fz[i]

    有一个结论,在j-i中会有一个t,使得j--t都是b[j],t+1--i都是b[i],

    然后就是暴力即可。

 1 #include<cstring>
 2 #include<cmath>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cstdio>
 6 #include<vector>
 7 
 8 #define ll long long
 9 #define N 350007
10 using namespace std;
11 inline int read()
12 {
13     int x=0,f=1;char ch=getchar();
14     while(ch>'9'||ch<'0'){if (ch=='-') f=-1;ch=getchar();}
15     while(ch<='9'&&ch>='0')
16     {
17         x=(x<<3)+(x<<1)+ch-'0';
18         ch=getchar();
19     }
20     return x*f;
21 }
22 
23 int n;
24 ll g[N],s1[N],s2[N];
25 int a[N],b[N],num,f[N],fz[N];
26 vector<int>ve[N];
27 
28 void mid_find(int x,int i)
29 {
30     int l=1,r=num;
31     while(l<r)
32     {
33         int mid=(l+r)>>1;
34         if (f[mid]<=x) l=mid+1;
35         else r=mid;
36     }
37     if (f[r]<=x) f[++num]=x,fz[i]=num;
38     else f[r]=x,fz[i]=r;
39 }
40 void solve_lis()
41 {
42     for (int i=1;i<=n;i++)
43     {
44         b[i]=a[i]-i,g[i]=(1LL<<60);
45         mid_find(b[i],i);
46         //fz[i]=num;
47         ve[fz[i]].push_back(i);
48     }
49     printf("%d\n",n-fz[n]);
50 }
51 void solve()
52 {
53     b[0]=-(1<<30);ve[0].push_back(0);
54     for (int i=1;i<=n;i++)
55     {
56         for (int j=0;j<ve[fz[i]-1].size();j++)
57         {
58             int pst=ve[fz[i]-1][j];
59             if (b[pst]>b[i]) continue;
60             for (int k=pst;k<=i;k++)
61                 s1[k]=abs(b[k]-b[pst]),s2[k]=abs(b[k]-b[i]);
62             for (int k=pst+1;k<=i;k++)
63                 s1[k]+=s1[k-1],s2[k]+=s2[k-1];
64             for (int k=pst;k<i;k++)
65                 g[i]=min(g[i],g[pst]+s1[k]-s1[pst]+s2[i]-s2[k]);        
66         }
67     }
68     printf("%lld",g[n]);
69 }
70 int main()
71 {
72     n=read();for (int i=1;i<=n;i++) a[i]=read();a[++n]=(1<<30);
73     solve_lis();
74     solve();
75 }

 

posted @ 2017-12-12 21:10  Kaiser-  阅读(264)  评论(0编辑  收藏  举报