洛谷P2501 bzoj1049 [HAOI2006]数字序列

题目链接

bzoj
洛谷

题解

第一问:

假如 \(i < j\)
如果 \(j\)能从\(i\)转移过来
显然中间空隙必须足够
例如:\(50\) \(53\) \(53\) \(52\)
就不能留下\(50\)\(52\)

那么可以得到如果\(j\)能从\(i\)转移过来
满足
\(a[j]-a[i] >= j-i\)
\(=>\) \(a[j]-j >= a[i]-i\)

那么可以对于以上数列跑一边最长不下降子序列

第二问:

将原序列变为上升序列,就等于将新的序列(\(b_i = a_i-i\))变成不下降序列

那么对于最长不下降子序列相邻两点 \(c_{i-1},c_i\)

显然\(b\)序列上, 在这两点之间的点不会存在 \(c_{i-1}<=x<=c_i\)

可以证明,存在一个\(k\)点, 使得 在\(c_{i-1}\)\(k\)点之间的点变成\(c_{i-1}\),剩下的点变成\(c_i\)
代价最小

枚举\(k\),算出最小值即可

注意最长不下降子序列不只有一种,需要对于每一种都做一次

Code

#include<bits/stdc++.h>
#define MAX 35010
using namespace std;
inline int read() {
	register int x=0,t=1;
	register char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-'){t=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*t;
}
struct Line {
	int v,next;
}e[MAX<<2];
int h[MAX],cnt=1,n,H[MAX],Q[MAX],len;
inline void Add(int u,int v) {
	e[cnt]=(Line){v,h[u]};
	h[u]=cnt++;
}
int f[MAX];
long long g[MAX],sum1[MAX],sum2[MAX];
int main()
{
	n=read();
	for(int i=1;i<=n;++i)H[i]=read()-i;
	H[++n]=1<<30;
	for(int i=1;i<=n;++i) {
		int l=1,r=len,ans=0;
		while(l<=r) {
			int mid=(l+r)>>1;
			if(Q[mid]>H[i])r=mid-1,ans=mid;
			else l=mid+1;
		}
		if(!ans)ans=++len;
		Q[f[i]=ans]=H[i];
	}
	printf("%d\n",n-len);
	for(int i=n;i>=0;--i)
		g[i]=1e18,Add(f[i],i);
	g[0]=0;H[0]=-H[n];
	for(int i=1;i<=n;++i) {
		for(int E=h[f[i]-1];E;E=e[E].next) {
			int v=e[E].v;
			if(v>i)break;
			if(H[v]>H[i])continue;
			for(int j=v;j<=i;++j) {
				sum1[j]=sum1[j-1]+abs(H[j]-H[v]);
				sum2[j]=sum2[j-1]+abs(H[i]-H[j]);
			}
			for(int j=v;j<i;++j)
				g[i]=min(g[i],g[v]+sum1[j]-sum1[v]+sum2[i]-sum2[j]);
		}
	}
	printf("%lld\n",g[n]);
	return 0;
}
posted @ 2018-12-19 15:25  zzy2005  阅读(117)  评论(0编辑  收藏  举报