Luogu P2501 [HAOI2006]数字序列

题目
首先把(a)改成严格单调上升等于把(a_i-i)改成单调不降。
那么第一问可以直接做LIS,答案就是(n-)LIS的长度。
同时我们记录一下序列中每个位置结尾的LIS长度。
第二问我们考虑这样一个事实:
对于LIS中相邻的两个数(a_i,a_j),这两个数中间的数一定要么(<a_i)要么(>a_j)
考虑一种修改方案,显然修改后中间的数会呈现阶梯状。
对于任意一个阶梯((l,r,x)),如果它上面的数(([l,r])(>x)的数)的个数大于下面的数的个数(([l,r])(<x)的数),那么我们把它往上移到下一个阶梯的高度一定会更优。
反之我们把它移到下一个阶梯的高度一定会更优。
如果上面和下面的数个数相等,我们随便怎么移动都不会改变这个代价。
因此最终我们一定可以把它移成两个阶梯((i+1,k,a_i),(k+1,j,a_j))
那么对于LIS中相邻的两数(a_i,a_j),最优的修改方案一定是找到某个(k),把(a_{i+1}sim a_k)改成(a_i),把(a_{k+1}sim a_j)改成(a_j)
我们枚举这个(k)。然后计算。
注意到数据随机,所以LIS的期望长度为(log n),因此复杂度正确。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int N=35007,inf=0x3f3f3f3f;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
ll min(ll a,ll b){return a<b? a:b;}
int abs(int a){return a<0? -a:a;}
int a[N],num[N],f[N];ll g[N],s[N],t[N];vector<int>E[N];
int main()
{
    int n=read(),i,k,l,len;
    for(i=1;i<=n;++i) a[i]=read()-i;
    a[++n]=inf,a[0]=-inf,memset(num,0x3f,sizeof num),num[0]=-inf,num[1]=a[1],f[1]=len=1,memset(g,0x3f,sizeof g),g[0]=0;
    for(i=2;i<=n;++i) l=upper_bound(num,num+len+1,a[i])-num,len=max(len,l),f[i]=l,num[l]=min(num[l],a[i]);
    for(i=0;i<=n;++i) E[f[i]].pb(i);
    for(i=1;i<=n;++i)
	for(int j:E[f[i]-1])
	{
	    if(j>i||a[j]>a[i]) continue;
	    for(k=j;k<=i;++k) s[k]=abs(a[k]-a[j]),t[k]=abs(a[k]-a[i]);
            for(k=j+1;k<=i;++k) s[k]+=s[k-1],t[k]+=t[k-1];
            for(k=j;k<i;++k) g[i]=min(g[i],g[j]+s[k]-s[j]+t[i]-t[k]);
	}
    printf("%d
%lld",n-f[n],g[n]);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11762870.html