bzoj 1049 [HAOI2006]数字序列
【bzoj1049】[HAOI2006]数字序列
Description
现在我们有一个长度为n的整数序列A。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。但是不希望改变过多的数,也不希望改变的幅度太大。
Input
第一行包含一个数n,接下来n个整数按顺序描述每一项的键值。
Output
第一行一个整数表示最少需要改变多少个数。 第二行一个整数,表示在改变的数最少的情况下,每个数改变的绝对值之和的最小值。
Sample Input
4
5 2 3 5
5 2 3 5
Sample Output
1
4
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 }