BZOJ1049:[HAOI2006]数字序列(DP)
Description
现在我们有一个长度为n的整数序列A。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。
但是不希望改变过多的数,也不希望改变的幅度太大。
Input
第一行包含一个数n,接下来n个整数按顺序描述每一项的键值。n<=35000,保证所有数列是随机的
Output
第一行一个整数表示最少需要改变多少个数。 第二行一个整数,表示在改变的数最少的情况下,每个数改变
的绝对值之和的最小值。
Sample Input
4
5 2 3 5
5 2 3 5
Sample Output
1
4
4
Solution
Code
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #define N (35009) 5 #define LL long long 6 using namespace std; 7 8 struct Edge{int to,next;}edge[N]; 9 int n,top,stack[N],f[N],a[N]; 10 LL g[N],s1[N],s2[N]; 11 int head[N],num_edge; 12 13 void add(int u,int v) 14 { 15 edge[++num_edge].to=v; 16 edge[num_edge].next=head[u]; 17 head[u]=num_edge; 18 } 19 20 void Solve1() 21 { 22 a[++n]=2e9; stack[0]=-2e9; 23 for (int i=1; i<=n; ++i) 24 if (a[i]>=stack[top]) stack[++top]=a[i],f[i]=top; 25 else 26 { 27 int l=1,r=top,ans=-1; 28 while (l<=r) 29 { 30 int mid=(l+r)>>1; 31 if (stack[mid]<=a[i]) l=mid+1; 32 else ans=mid,r=mid-1; 33 } 34 stack[ans]=a[i]; f[i]=ans; 35 } 36 printf("%d\n",n-top); 37 } 38 39 void Solve2() 40 { 41 for (int i=n; i>=0; --i) 42 g[i]=2e18,add(f[i],i); 43 a[0]=-2e9; g[0]=0; 44 for (int i=1; i<=n; ++i) 45 for (int j=head[f[i]-1]; j; j=edge[j].next) 46 { 47 int pre=edge[j].to; 48 if (pre>i) break; 49 if (a[pre]>a[i]) continue; 50 s1[pre-1]=s2[pre-1]=0; 51 for (int k=pre; k<=i; ++k) 52 { 53 s1[k]=s1[k-1]+abs(a[k]-a[pre]); 54 s2[k]=s2[k-1]+abs(a[k]-a[i]); 55 } 56 for (int k=pre; k<i; ++k) 57 g[i]=min(g[i],g[pre]+s1[k]-s1[pre]+s2[i]-s2[k]); 58 } 59 printf("%lld\n",g[n]); 60 } 61 62 int main() 63 { 64 scanf("%d",&n); 65 for (int i=1; i<=n; ++i) 66 scanf("%d",&a[i]),a[i]-=i; 67 Solve1(); Solve2(); 68 }