Ciclo Mínimo Creciente#12253

Ciclo Mínimo Creciente (Editorial)

Autor del Problema:

Hasan Saleh (MathModel)

Preparado por:

Hasan Saleh (MathModel)

Editado por:

Hasan Saleh (MathModel)

Solución

Solo nos importa qué índices mantenemos (0 ops) y cuáles arreglamos de una vez (1 op). Una operación sobre a_i permite a_i\leftarrow(a_i+t)\bmod k con t\ge m, y dado que podemos agregar cualquier múltiplo de encima, una op puede establecer a_i a cualquier valor en [0,k-1]. Así que:

  • índice mantenido: el valor se mantiene como a_i,

  • índice cambiado: el valor se convierte en cualquier cosa en [0,k-1].

Restricción crucial:

  • Si mantenemos algún a_i\ge k, entonces todo a su derecha también debe ser mantenido y ya ser no decreciente; de lo contrario, un valor cambiado (\le k-1) tendría que estar después de , lo que puede romper el orden. Así que un índice con a_i\ge k es elegible para mantener solo si el sufijo ya es no decreciente.

  • Si a_i<k, siempre es elegible para mantener (los vecinos cambiados pueden ser elegidos dentro de [0,k-1] para ajustarse).

Queremos maximizar la cantidad de índices mantenidos; el resto son cambiados. Cualquier conjunto mantenido debe ser no decreciente por valor. Por lo tanto, tomamos el LIS (variante no decreciente, es decir, versión ) sobre la secuencia de posiciones elegibles:

\text{elegible}(i):\quad (a[i]<k)\ \ \text{o}\ \ (\text{suf}[i]=0),

donde cuenta los descensos en . La respuesta es:

\text{ops}_{\min}=n-\text{LIS}.

¿Por qué?: los elementos mantenidos forman una subsecuencia no decreciente (nunca los alteramos), y cada otro lugar puede ser llenado por valores cambiados dentro de [0,k-1] para mantener el orden. Si aparece un mantenido , la elegibilidad impone un sufijo no decreciente limpio sin entradas cambiadas después de él.

La complejidad es \mathcal{O}(n \log n)

def solve():
    n,m,k=map(int,input().split())
    a=list(map(int,input().split()))
    suf=[0]*n
    for i in range(n-2,-1,-1):
        suf[i]=suf[i+1]+(a[i]>a[i+1])
    INF=10**18
    dp=[INF]*(n+1)
    dp[0]=-INF
    lis=0
    for i in range(n):
        if a[i]<k or suf[i]==0:
            l,r,b=1,n,0
            while l<=r:
                mid=(l+r)//2
                if dp[mid]<=a[i]:
                    b=mid;l=mid+1
                else:
                    r=mid-1
            if a[i]<dp[b+1]: dp[b+1]=a[i]
            if b+1>lis: lis=b+1
    print(n-lis)

for _ in range(int(input())):
    solve()