题意:一个数组a(长度为n),还有数组b(长度为k),其中数组b中存的是位置,在数组a中这些位置的数不可以改变,其余位置的数可以随意改变,问能否是a变成一个上升序列,能的话最小的操作次数是多少
首先判断一下能否使a变成上升序列(略)
然后将a划分为k+1个区间,每个区间单独计算
将每个区间处理一下
比如一段序列:2、3、5、7、8、6、10
每个数减去其相应的位置
然后序列变为:1、1、2、3、3、0、3
可以看出该序列的最长上升子序列为处理后的最长不下降子序列,其中该区间是被b数组固定的,因此该区间前后还有b数组固定的数,因此该区间的最长不下降子序列的值要大于等于a[b[i-1]]-1和a[b[i]]-(b[i]-b[i-1]+1
代码:
#include <bits/stdc++.h>
#define ll long long
#define IOS std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
ll n,k,ans;
ll a[500005],b[500005];
ll c[500005],f[500005];
ll inf=0x3f3f3f3f3f3f3f;
void dp(ll st,ll en,ll x,ll y)
{
ll cnt=0,val=2,ip=0,id=-1;
for(int i=x;i<=y;i++)
{
c[++cnt]=a[i]-val;
val++;
}
for(int i=1;i<=cnt;i++)
{
if(c[i]>=st&&c[i]<=en){
id=i;break;}
}
if(id==-1){
ans+=(y-x+1);return;}
f[ip++]=c[id];
for(int i=id+1;i<=cnt;i++)
{
if(c[i]>en||c[i]<st){
continue;}
if(c[i]>=f[ip-1]){
f[ip++]=c[i];
}
else{
ll pos=upper_bound(f,f+ip,c[i])-f;
f[pos]=c[i];
}
}
ans+=(y-x+1-ip);
return;
}
int main()
{
scanf("%lld %lld",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);}
for(int i=1;i<=k;i++)
{
scanf("%lld",&b[i]);}
if(k==0)//特判k=0
{
ll ip=0;
for(int i=1;i<=n;i++){
c[i]=a[i]-i;}
f[ip++]=c[1];
for(int i=2;i<=n;i++)
{
if(c[i]>=f[ip-1])
{
f[ip++]=c[i];}
else
{
ll pos=upper_bound(f,f+ip,c[i])-f;
f[pos]=c[i];
}
}
printf("%lld\n",n-ip);
return 0;
}
ll flag=1;//判断结果是否为-1
for(int i=2;i<=k;i++)
{
if(a[b[i]]<=a[b[i-1]]){
flag=0;break;}
if(b[i]-b[i-1]-1>a[b[i]]-a[b[i-1]]-1){
flag=0;break;}
}
if(!flag){
printf("-1\n");return 0;}
for(int i=1;i<=k;i++)
{
if(i==1){
ll st=-inf,en=a[b[i]]-b[i]-1;
dp(st,en,1,b[i]-1);
}
else{
ll st=a[b[i-1]]-1,en=a[b[i]]-(b[i]-b[i-1]+1);
dp(st,en,b[i-1]+1,b[i]-1);
}
}
dp(a[b[k]]-1,inf,b[k]+1,n);
printf("%lld\n",ans);
return 0;
}