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