题意:给出一个长度为n的序列,k个位置,代表序列里这些位置上的数是固定的。每次可以修改序列里没被固定的一个数,问最少几次修改可以使得整个序列变成严格递增的,或者不可能。
两个小结论
1、把一个序列A变成非严格单调递增(单调不下降的),至少需要修改的数个数为序列A的总长度减去A的最长不下降子序列长度。
2、把一个序列变成严格单调递增的,至少需要修改的数个数,构造序列B[i]=A[i]-i,答案为序列总长度减去B的最长不下降子序列长度。
思路:这道题如果把固定一些数这个条件去掉的话就是看成上面结论2一样的问题了,但是仔细观察可以发现k个数将整个序列分为了k+1个部分,因为k个数是固定的,所以这k+1个部分其实是互不影响的,可以把他们拿出来单独做,就变成和上面结论2里一样的问题了。唯一有区别的是,在求解最长不下降子序列的时候,首位和末位是被固定的。所以在使用二分法求解时要一些变化。具体实现就是先令 d[1] = a[l-1] ,且 d[1] 不允许修改,按照正常的流程计算完 [l,r] 这段区间的 lis,然后找到小于等于a[r+1]的最大位置,此时的长度就是答案了。可以在整个序列的首尾设置一些哨兵元素这样就不需要特判了。
AC代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; #define X first #define Y second #define pb push_back #define pll pair<ll, ll> #define pli pair<ll, int> #define pii pair<int,int> #define New_Time srand((unsigned)time(NULL)) inline ll gcd(ll a, ll b) { while (b != 0) { ll c = a % b; a = b; b = c; }return a < 0 ? -a : a; } inline ll lowbit(ll x) { return x & (-x); } //int head[2000010], Edge_Num; //struct Edge { int to, next; ll w; }e[4000010]; //inline void ade(int x, int y, ll w) { e[++Edge_Num] = { y,head[x],w }; head[x] = Edge_Num; } //inline void G_init(int n) { memset(head, 0, sizeof(int) * (n + 100)); Edge_Num = 0; } int dir[8][2] = { {-1,0},{0,-1},{-1,-1},{1,-1},{1,0},{0,1},{1,1},{-1,1} }; const long double PI = 3.14159265358979323846; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; inline ll rd() { ll x = 0; bool f = 1; char ch = getchar(); while (ch < '0' || ch>'9') { if (ch == '-')f = 0; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); } return f ? x : -x; } const double eps = 1e-8; const ll mod = 1e9 + 7; const int M = 1e6 + 10; const int N = 1e6 + 10; int a[N], b[N]; int n, k; vector<int>lis; int gao(int l, int r) { if (b[r + 1] - b[l - 1] - 1 < r - l + 1)return -1; lis.clear(); lis.pb(a[l - 1]); for (int i = l; i <= r; i++) { if (a[i] >= lis.back())lis.pb(a[i]); else { int p = upper_bound(lis.begin(), lis.end(), a[i]) - lis.begin(); if (p == 0)continue; lis[p] = a[i]; } } int pos = upper_bound(lis.begin(), lis.end(), a[r + 1]) - lis.begin() + 1; int len = (r + 1) - (l - 1) + 1; return len - pos; } bool check(int l, int r) { for (int i = l; i < r; i++) { if (b[i] >= b[i + 1])return 0; } return 1; } void solve() { n = rd(), k = rd(); for (int i = 1; i <= n; i++) { b[i] = a[i] = rd(); a[i] -= i; } b[0] = a[0] = -inf; b[n + 1] = a[n + 1] = inf; for (int i = 1; i <= k; i++) { vis[rd()] = 1; } int l = 0, r = 0; for (int i = 1; i <= n; i++) { if (vis[i] && (i == 1 || !vis[i - 1])) { l = i; } if (vis[i] && (i == n || !vis[i + 1])) { r = i; if (!check(l, r)) { puts("-1"); return; } } } l = 0, r = 0; int ans = 0; for (int i = 1; i <= n; i++) { if (!vis[i] && (i == 1 || vis[i - 1])) { l = i; } if (!vis[i] && (i == n || vis[i + 1])) { r = i; int x = gao(l, r); if (x == -1) { puts("-1"); return; } ans += x; } } cout << ans << endl; } int main() { int _T = 1; //_T = rd(); while (_T--)solve(); }