P2501 [HAOI2006]数字序列(LIS&贪心)
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2501 [HAOI2006]数字序列(LIS&贪心)相关的知识,希望对你有一定的参考价值。
P2501 [HAOI2006]数字序列(LIS&贪心)
对于一个区间 [ l , r ] [l,r] [l,r],显然需要满足 a r − a l ≥ r − l a_r-a_l\\ge r-l ar−al≥r−l 才有可能严格递增。
移项: a r − r ≥ a l − l a_r-r\\ge a_l-l ar−r≥al−l。
令 b i = a i − i b_i=a_i-i bi=ai−i,在数组 b b b中到最长非递减子序列长度 c n t cnt cnt, n − c n t n-cnt n−cnt就是最少修改的数。
第二问,有一个结论,对于 L I S LIS LIS中相邻的两点,肯定有个分界点 k k k, [ l , k ] [l,k] [l,k]修改成 a l a_l al, [ k + 1 , r ] [k+1,r] [k+1,r]修改成 a r a_r ar 是最优的。
所以可以预处理修改成 a l , a r a_l,a_r al,ar的前缀和。
然后扫一遍取最值。
为了方便找到子序列中的前一个,我们给每个 f i , i f_i,i fi,i建边 e d g e ( f i , i ) edge(f_i,i) edge(fi,i)。
这样每次要找到 f i − 1 f_{i}-1 fi−1的位置,就可以直接访问 u = f i − 1 u=f_i-1 u=fi−1所连的边。
时间复杂度: O ( n 2 + n l o g n ) O(n^2+nlogn) O(n2+nlogn)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=35010;
int n,cnt;
int head[maxn],a[maxn],b[maxn],f[maxn],g[maxn],sum1[maxn],sum2[maxn];
struct edge
{
int to,nxt;
}e[maxn];
inline void add(int u,int v)
{
e[++cnt].nxt=head[u];
head[u]=cnt;
e[cnt].to=v;
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]),a[i]-=i;
a[++n]=0x3f3f3f3f;a[0]=-0x3f3f3f3f;
memset(b,0x3f,sizeof(b));b[0]=-0x3f3f3f3f,b[1]=a[1];
int len=1;
f[1]=1;
for(int i=2;i<=n;i++)
{
int tmp=upper_bound(b,b+len+1,a[i])-b;
len=max(len,tmp);
f[i]=tmp;
b[tmp]=a[i];
}
printf("%lld\\n",n-len);
for(int i=0;i<=n;i++)add(f[i],i);
memset(g,0x3f,sizeof(g));g[0]=0;
for(int i=1;i<=n;i++)
{
for(int j=head[f[i]-1];j;j=e[j].nxt)
{
int y=e[j].to;
if(y>i||a[y]>a[i])continue;
for(int k=y;k<=i;k++)sum1[k]=abs(a[k]-a[y]),sum2[k]=abs(a[k]-a[i]);
for(int k=y+1;k<=i;k++)sum1[k]+=sum1[k-1],sum2[k]+=sum2[k-1];
for(int k=y;k<=i-1;k++)g[i]=min(g[i],g[y]+sum1[k]-sum1[y]+sum2[i]-sum2[k]);
}
}
printf("%lld",g[n]);
return 0;
}
以上是关于P2501 [HAOI2006]数字序列(LIS&贪心)的主要内容,如果未能解决你的问题,请参考以下文章