Suitable Edit for LIS
Explicación
Podemos resolver este problema con la técnica de LIS con Árbol de Segmentos.
Si la solución óptima contiene una actualización en el índice , actualizaremos a si y a en caso contrario. Si tenemos alguna subsecuencia creciente que contiene el valor y queremos agregar un nuevo elemento justo después de , es óptimo elegir . Esto se debe a que maximiza la cantidad de opciones para el siguiente elemento mientras sigue siendo . Siempre es óptimo actualizar a porque si sigue directamente a , no debemos tomar ningún índice entre el índice de y . Actualizar el valor en el índice a hace que el original sea el único valor que no podemos tomar. Si actualizamos el valor en un índice posterior, no podremos tomar un conjunto más grande de valores que aún incluya .
Para , es óptimo poner porque cualquier valor mayor de hará que el conjunto de posibles valores siguientes en la LIS sea un subconjunto del conjunto de posibles valores siguientes en la LIS cuando .
Podemos usar Árboles de Segmentos. Denotémoslos como y . guardará la LIS que termina con el valor sin ninguna operación realizada. guardará la LIS que termina con el valor después de realizar una operación. También denotemos como el mejor reemplazo para .
Podemos transicionar de la siguiente forma:
La primera y la tercera transición extienden la LIS. La segunda transición se usa para aplicar la operación. Nuestro resultado final es el valor máximo en .
Notamos que, como , necesitaremos usar compresión de coordenadas.
Implementación
Complejidad temporal:
#include <bits/stdc++.h>
using namespace std;
// BeginCodeSnip{Range Maximum Segment Tree}
class MaxSegTree {
private:
int len;
vector<int> segtree;
public:
MaxSegTree(int len) : len(len), segtree(2 * len) {}
void set(int ind, int val) {
ind += len;
for (segtree[ind] = max(segtree[ind], val); ind > 1; ind >>= 1) {
segtree[ind >> 1] = max(segtree[ind], segtree[ind ^ 1]);
}
}
int range_max(int from, int to) {
int max_ = 0;
for (from += len, to += len; from < to; from >>= 1, to >>= 1) {
if ((from & 1) != 0) { max_ = max(max_, segtree[from++]); }
if ((to & 1) != 0) { max_ = max(max_, segtree[--to]); }
}
return max_;
}
};
// EndCodeSnip
int main() {
int n;
cin >> n;
vector<int> a(n);
map<int, int> compressed;
vector<int> best(n, 0);
for (int i = 0; i < n; i++) { cin >> a[i]; }
vector<int> val = a;
val.push_back(0);
for (int i = 0; i < n - 1; i++) { val.push_back(val[i] + 1); }
sort(val.begin(), val.end());
val.erase(unique(val.begin(), val.end()), val.end());
for (int i = 0; i < val.size(); i++) { compressed[val[i]] = i; }
for (int i = 1; i < n; i++) { best[i] = a[i - 1] + 1; }
MaxSegTree before(2 * n);
MaxSegTree after(2 * n);
for (int i = 0; i < n; i++) {
after.set(compressed[a[i]], after.range_max(0, compressed[a[i]]) + 1);
after.set(compressed[best[i]], before.range_max(0, compressed[best[i]]) + 1);
before.set(compressed[a[i]], before.range_max(0, compressed[a[i]]) + 1);
}
cout << after.range_max(0, 2 * n) << '\n';
return 0;
}