1049: [HAOI2006]数字序列
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1813 Solved: 789
[Submit][Status][Discuss]
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
被细节恶心到啦。
第一问可以补集转换,减下标然后求最长不下降子序列,用总序列长度-lis长度水过,关键是这个第二问呐。。
有个并不显然的显然结论:
若j<i&&f[j]+1==f[i]&&a[j]<=a[i](保证j可以转移到i)
那么把这段区间中的数值改成a[j]或者a[i]一定是最优的,跑个dp决策
这里有证明
http://pan.baidu.com/share/link?uk=2651016602&shareid=1490516411
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define N 35005 4 using namespace std; 5 int n,mx,a[N],b[N],f[N],hd[N],nxt[N];ll g[N],s1[N],s2[N]; 6 int find(int x){ 7 int l=1,r=mx,mid,ans=0; 8 while(l<=r){ 9 if(b[mid=(l+r)>>1]<=x)l=(ans=mid)+1; 10 else r=mid-1; 11 }return ans; 12 } 13 void dp(){ 14 memset(b,0x3f,sizeof(b)); 15 b[0]=-1<<30; 16 for(int i=1;i<=n;i++){ 17 int p=find(a[i]); 18 mx=max(mx,f[i]=p+1); 19 b[p+1]=min(b[p+1],a[i]); 20 } 21 } 22 void add(int x,int y){nxt[y]=hd[x];hd[x]=y;} 23 void solve(){ 24 //memset(g,0x3f,sizeof(g));g[0]=0; 25 for(int i=1;i<=n;i++)g[i]=1ll<<60; 26 memset(hd,-1,sizeof(hd)); 27 for(int i=n;i>=0;i--)add(f[i],i); 28 for(int i=1;i<=n;i++){ 29 for(int j=hd[f[i]-1];~j&&j<i;j=nxt[j]){ 30 if(a[j]>a[i])continue; 31 for(int k=j;k<=i;k++) 32 s1[k]=abs(a[k]-a[j]),s2[k]=abs(a[k]-a[i]); 33 for(int k=j+1;k<=i;k++) 34 s1[k]+=s1[k-1],s2[k]+=s2[k-1]; 35 for(int k=j;k<i;k++) 36 g[i]=min(g[i],g[j]+s1[k]-s1[j]+s2[i]-s2[k]); 37 } 38 } 39 } 40 int main(){ 41 scanf("%d",&n);a[0]=-1<<30; 42 for(int i=1;i<=n;i++) 43 scanf("%d",&a[i]),a[i]-=i; 44 a[++n]=1<<30; 45 dp();solve(); 46 printf("%d %lld ",n-mx,g[n]); 47 return 0; 48 }