codeforces 1437 E. Make It Increasing (最长上升子序列)
题目链接:https://codeforces.com/contest/1437/problem/E
我们发现,每个不能改的位置之间的块是互不影响的
所以我们考虑单独计算每个块内的答案
对于每个块,如果元素小于边界(左右两边不能修改的元素大小),那么是必须要修改的,
剩下的元素,我们要修改尽量少的元素
没错,就是序列长度减去最长上升子序列的数量
然而还有一个问题,最长上升子序列的某两个元素大小可能是相邻的,
但是对应到原序列中,可能这两个元素之间可能还有别的元素,这样就无法修改中间元素了
这里有一个小trick(看题解才明白): 如果最后 \(a[i]\) 是严格单调增的,那么 \(a[i] - i\) 一定是不降的,
所以可以令 \(c[i] = a[i] - i\),去求解 \(c[i]\) 的最长不降子序列,这样就可以保证选出的最长上升子序列之间的元素肯定可以改变
最后再判断一下什么时候无解就好了
时间复杂度\(O(nlogn)\)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 500010;
int n,k,cnt = 0; int inf; int ans;
int a[maxn],b[maxn],c[maxn],d[maxn],dp[maxn],g[maxn];
void DP(int l,int r){
if(r<l) return;
for(int i=l;i<=r;++i){
if(c[i] <= c[r+1] && c[i] >= c[l-1]){
int pos = upper_bound(dp + l, dp + r, c[i]) - dp;
dp[pos] = c[i];
}
}
int flag = 0;
for(int i=r;i>=l;--i){
if(dp[i] != inf){
ans += (r - l + 1) - (i - l + 1);
flag = 1;
break;
}
}
if(!flag) ans += r-l+1;
}
ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
int main(){
ans = 0;
memset(dp,0x3f,sizeof(dp));
inf = dp[0];
dp[0] = -inf;
int flag = 0;
n = read(), k = read();
for(int i=1;i<=n;++i){
a[i] = read();
c[i] = a[i] - i;
}
c[0] = -inf; c[n+1] = inf;
for(int i=1;i<=k;++i){
b[i] = read();
d[++cnt] = a[b[i]];
}
for(int i=1;i<=cnt;++i){
int pos = lower_bound(dp + 1, dp + cnt, d[i]) - dp;
dp[pos] = d[i];
}
if(dp[cnt] == inf){
printf("-1\n");
}
else{
int ff = 0;
memset(dp,0x3f,sizeof(dp));
dp[0] = -inf;
for(int i=1;i<=k;++i){
DP(b[i-1]+1,b[i]-1);
if(i > 1 && b[i] - b[i-1] - 1 > 0 && b[i] - b[i-1] - 1 > a[b[i]] - a[b[i-1]] - 1){
ff = 1;
printf("-1\n");
break;
}
}
if(b[k] <= n - 1) DP(b[k]+1,n);
if(!ff) printf("%d\n",ans);
}
return 0;
}