【BZOJ1049】 [HAOI2006]数字序列

BZOJ1049 [HAOI2006]数字序列


dp好题?

第一问

第一问我会做!令\(b_i=a_i-i\),求一个最长不下降子序列.

\(n-ans\)就是最终的答案.

第二问

好难啊.不会.挖坑待补.

考虑一下对于一个ij的可能符合情况,定然存在一个$k$在ik之中为\(a_i\),k~j之中为\(a_j\).

然后就可以dp了.

这个转移比较玄学.如果不随机就GG了.

随机的证明

代码实现

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define ll long long
#define re register
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
inline int gi()
{
	int f=1,sum=0;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return f*sum;
}
const int N=50010;
int a[N],n,L,cnt,mn[N],f[N],front[N],to[N<<2],nxt[N<<2];
ll g[N],s1[N],s2[N];
int find(int x){
	int l=1,r=L,t=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(mn[mid]<=x)t=mid,l=mid+1;
		else r=mid-1;
	}
	return t;
}
void dp(){
	memset(mn,127,sizeof(mn));
	mn[0]=-(1<<30);
	for(int i=1;i<=n;i++){
		int q=find(a[i]);
		f[i]=q+1;
		L=max(L,f[i]);
		mn[q+1]=min(mn[q+1],a[i]);
	}
}
void Add(int u,int v){
	to[++cnt]=v;nxt[cnt]=front[u];front[u]=cnt;
}
void solve(){
	for(int i=n;~i;i--){
		Add(f[i],i);
		g[i]=1ll<<60;
	}
	g[0]=0;a[0]=-(1<<30);
	for(int u=1;u<=n;u++)
		for(int i=front[f[u]-1];i;i=nxt[i]){
			int v=to[i];
			if(v>u)break;
			if(a[v]>a[u])continue;
			for(int j=v;j<=u;j++)s1[j]=abs(a[v]-a[j]),s2[j]=abs(a[u]-a[j]);
			for(int j=v+1;j<=u;j++)
				s1[j]+=s1[j-1],s2[j]+=s2[j-1];
			for(int j=v;j<u;j++)
				g[u]=min(g[u],g[v]+s1[j]-s1[v]+s2[u]-s2[j]);
		}
}
int main(){
	n=gi();
	for(int i=1;i<=n;i++)a[i]=gi()-i;
	a[++n]=1<<30;
	dp();solve();
	printf("%d\n%lld\n",n-f[n],g[n]);
	return 0;
}
posted @ 2019-01-24 15:16  QwQGJH  阅读(188)  评论(0编辑  收藏  举报