洛谷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-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
",g[n]);
	return 0;
}
原文地址:https://www.cnblogs.com/zzy2005/p/10143547.html