---
# System prepended metadata

title: DP (Div. B)
tags: [Notas de clase, Tutoriales, TCMX 2024]

---

# DP (Div. B)
## Repaso de máscaras de bits
Hay problemas de DP donde utilizamos números enteros como máscaras de bits para representar conjuntos de elementos cuando el universo es pequeño (al rededor de $20$ elementos por lo general). Si en la representación binaria está prendido el bit $i$, diremos que el $i$-ésimo elemento está en el conjunto.

Las operaciones de conjuntos se pueden implementar en máscaras de bits utilizando operaciones a nivel de bits. Digamos que $mk1$ es la máscara conteniendo los elementos de $A$ y $mk2$ es la máscara con los elementos de $B$, entonces:

- Para determinar pertenencia $i \in A$, se escribe `bool(mk1 & (1 << i))`.
- La unión $A \cup B$ es `mk1 | mk2`.
- La intersección $A \cap B$ es `mk1 & mk2`.
- La cardinalidad $|A|$ es `__builtin_popcount(mk1)`.
- Para determinar si $A \subseteq B$ se escribe `bool((mk1 & mk2) == mk1)`.
- Para determinar si $A \supseteq B$ se escribe `bool((mk1 & mk2) == mk2)`.
- Para insertar el elemento $i$ en $A$ se escribe `mk1 |= 1 << i` (ausimiendo que $i$ no está en $A$). Si no se está seguro si $i$ está en $A$, se escribe `mk1 |= 1 << i`.
- Para eliminar el elemento $i$ se $A$ se escribe `mk1 |= ~(1 << i)` (ausimiendo que $i$ está en $A$ actualmente). Si no se está seguro si $i$ está en $A$, se escribe `mk1 = mk1 & ~(1 << i)`.
- El complemento de $A$ asumiendo que el universo tiene $k$ elementos es `((1 << k)-1) ^ mk1`.

A lo largo de las notas de clase estaré utilizando los términos y la notación de conjuntos y sus operaciones equivalentes a nivel de bits de forma intercambeable, con excepción de las implementaciones donde forzosamente utilizaré las operaciones a nivel de bits.

## SOS DP
:::success
:information_source: **Problema 1**
Dado un arreglo $A=[a_0, a_1, \dots, a_{2^k-1}]$, para cada $i$ tal que $0 \leq i < 2^k$, hallar la suma de las submáscaras de $i$. Dicho de otra forma, queremos obtener un arreglo $A'=[a'_0, a'_1, \dots, a'_{2^k-1}]$, tal que:

$$
a'_i=\sum_{s \subseteq i} a_s
$$

**Ejemplo:**
Sea $A=[4,7,1,0,2,3,1,5]$. Entonces los elementos de $A'$ serían:

$a'_0=a_0=4$
$a'_1=a_0+a_1=4+7=11$
$a'_2=a_0+a_2=4+1=5$
$a'_3=a_0+a_1+a_2+a_3=4+7+1+0=12$
$a'_4=a_0+a_4=4+2=6$
$a'_5=a_0+a_1+a_4+a_5=4+7+2+3=16$
$a'_6=a_0+a_2+a_4+a_6=4+1+2+1=8$
$a'_7=a_0+a_1+a_2+a_3+a_4+a_5+a_6+a_7=4+7+1+0+2+3+1+5=23$

La respuesta debería ser el arreglo $A'=[4,11,5,12,6,16,8,23]$.
:::

Este es el problema que resuelve la DP conocida como Sum Over Subsets (SOS). A continuación veremos algunas soluciones.

### Solución $O(4^k)$
La solución más bruta es: para cada máscara $mk$, iterar sobre todas las máscaras y sumar aquellas que sean su submáscara:

```C++
for (int mk = 0; mk < (1 << k); mk++) {
    Ap[mk] = 0;
    for (int s = 0; s < (1 << k); s++)
        if ((mk & s) == s)
            Ap[mk] += A[s];
}
```

### Solución $O(3^k)$
En la solución anterior iteramos sobre todos los elementos de $A$ para obtener $b_{mk}$. Pero, ¿qué tanto podemos mejorar la solución si sólo iteramos por las submáscaras de $mk$? Esto lo podemos hacer de la siguiente forma:

```C++
for (int mk = 0; mk < (1 << k); mk++) {
    Ap[mk] = 0;
    for (int s = mk;; s = (s - 1) & mk) {
        Ap[mk] += A[s];
        if (!s)
            break;
    }
}
```

Veamos un ejemplo de como se recorren las submáscaras de $mk=154$:

:::spoiler Ejemplo


| Operacion | Máscara | Valor númerico | # submáscara |
| - | - | - | - |
| $s=mk$ | $\underline{1}00\underline{1}\underline{1}0\underline{1}0_2$ | $154$ | 15 |
| $s-1$ | $\underline{1}00\underline{1}\underline{1}0\underline{0}1_2$ | $153$ | |
| $s=(s-1)\&mk$ | $\underline{1}00\underline{1}\underline{1}0\underline{0}0_2$ | $152$ | $14$ |
| $s-1$ | $\underline{1}00\underline{1}\underline{0}1\underline{1}1_2$ | $151$ | |
| $s=(s-1)\&mk$ | $\underline{1}00\underline{1}\underline{0}0\underline{1}0_2$ | $146$ | $13$ |
| $s-1$ | $\underline{1}00\underline{1}\underline{0}0\underline{0}1_2$ | $145$ | |
| $s=(s-1)\&mk$ | $\underline{1}00\underline{1}\underline{0}0\underline{0}0_2$ | $144$ | $12$ |
| $s-1$ | $\underline{1}00\underline{0}\underline{1}1\underline{1}1_2$ | $143$ | |
| $s=(s-1)\&mk$ | $\underline{1}00\underline{0}\underline{1}0\underline{1}0_2$ | $138$ | $11$ |
| $s-1$ | $\underline{1}00\underline{0}\underline{1}0\underline{0}1_2$ | $137$ | |
| $s=(s-1)\&mk$ | $\underline{1}00\underline{0}\underline{1}0\underline{0}0_2$ | $136$ | $10$ |
| $s-1$ | $\underline{1}00\underline{0}\underline{0}1\underline{1}1_2$ | $135$ | |
| $s=(s-1)\&mk$ | $\underline{1}00\underline{0}\underline{0}0\underline{1}0_2$ | $130$ | $9$ |
| $s-1$ | $\underline{1}00\underline{0}\underline{0}0\underline{0}1_2$ | $129$ | |
| $s=(s-1)\&mk$ | $\underline{1}00\underline{0}\underline{0}0\underline{0}0_2$ | $128$ | $8$ |
| $s-1$ | $\underline{0}11\underline{1}\underline{1}1\underline{1}1_2$ | $127$ | |
| $s=(s-1)\&mk$ | $\underline{0}00\underline{1}\underline{1}0\underline{1}0_2$ | $26$ | $7$ |
| $s-1$ | $\underline{0}00\underline{1}\underline{1}0\underline{0}1_2$ | $25$ | |
| $s=(s-1)\&mk$ | $\underline{0}00\underline{1}\underline{1}0\underline{0}0_2$ | $24$ | $6$ |
| $s-1$ | $\underline{0}00\underline{1}\underline{0}1\underline{1}1_2$ | $23$ | |
| $s=(s-1)\&mk$ | $\underline{0}00\underline{1}\underline{0}0\underline{1}0_2$ | $18$ | $5$ |
| $s-1$ | $\underline{0}00\underline{1}\underline{0}0\underline{0}1_2$ | $17$ | |
| $s=(s-1)\&mk$ | $\underline{0}00\underline{1}\underline{0}0\underline{0}0_2$ | $16$ | $4$ |
| $s-1$ | $\underline{0}00\underline{0}\underline{1}1\underline{1}1_2$ | $15$ | |
| $s=(s-1)\&mk$ | $\underline{0}00\underline{0}\underline{1}0\underline{1}0_2$ | $10$ | $3$ |
| $s-1$ | $\underline{0}00\underline{0}\underline{1}0\underline{0}1_2$ | $9$ | |
| $s=(s-1)\&mk$ | $\underline{0}00\underline{0}\underline{1}0\underline{0}0_2$ | $8$ | $2$ |
| $s-1$ | $\underline{0}00\underline{0}\underline{0}1\underline{1}1_2$ | $7$ | |
| $s=(s-1)\&mk$ | $\underline{0}00\underline{0}\underline{0}0\underline{1}0_2$ | $2$ | $1$ |
| $s-1$ | $\underline{0}00\underline{0}\underline{0}0\underline{0}1_2$ | $1$ | |
| $s=(s-1)\&mk$ | $\underline{0}00\underline{0}\underline{0}0\underline{0}0_2$ | $0$ | $0$ |
:::

#### Complejidad
La complejidad de esta solución se puede obtener si analizamos sobre qué pares $(mk,s)$ iteran los dos ciclos `for` anidados. Hagamos el análisis bit por bit:

- Para los bits que sean $0$ en $mk$, sólo vamos a iterar sobre submáscaras $s$ que sean $0$ en esos bits.
- Para los bits que sean $1$ en $mk$, podemos iterar sobre submáscaras $s$ que tengan $0$ o $1$ en esos bits.

Entonces para cada bit, hay $3$ combinaciones posibles:
1. Que sea $0$ tanto en $mk$ como en $s$.
2. Que sea $1$ en $mk$ y $0$ en $s$.
3. Que sea $1$ tanto en $mk$ como en $s$.

Para cada bit tenemos $3$ combinaciones, entonces iteramos sobre $3^k$ parejas $(mk,s)$, por lo que la complejidad es $O(3^k)$.

### Solución $O(k2^k)$
Hasta el momento no hemos usado la técnica de DP para atacar el problema. ¿Qué función de DP podríamos definir para encontrar los elementos de $A'$?

Una forma de encontrar soluciones DP es pensar en una función recursiva de fuerza bruta, y después memorizar sobre los argumentos de la función. Una posible función que cumple con dichas características para este problema es $f(mask,i)$, la cual regresa la suma sobre todas las submáscaras de $mask$ que tienen los primeros $i$ bits iguales a los de $mask$ (y los demás bits pueden o no ser iguales).

La función quedaría definida como:

$$
f(mask,i) = 
\begin{cases}
a[mask] & \text{si $i = k$} \\
f(mask,i+1)+f(mask \text{ XOR } 2^i,i+1) & \text{si $i \in mask$} \\
f(mask,i+1) & \text{en caso contrario}
\end{cases}
$$

El valor de $a'_i$ es $f(mask,0)$.

$mask$ tiene $2^k$ valores distinos, $i$ tiene $k+1$ valores distintos, por lo tanto hay $(k+1)2^k)$ estados distintos. Las transiciones son en tiempo constante, por lo tanto la solución con DP queda en $O(k2^k)$.

La DP recursiva se puede programar facilmente teniendo la función $f(mask,i)$. La versión iterativa, quedaría de la siguiente forma:

```C++
vector<int> SOS_DP(vector<int> A, int k) {
    vector<vector<int>> dp(k + 1, vector<int>(1 << k));
    
    for (int mk = 0; mk < (1 << k); mk++)
        dp[k][mk] = A[mk];
    
    for (int i = k - 1; i >= 0; i--)
        for (int mk = 0; mk < (1 << k); mk++) {
            if (mk & (1 << i)) 
                dp[i][mk] = dp[i + 1][mk] + dp[i + 1][mk - (1 << i)];
            else
                dp[i][mk] = dp[i + 1][mk];
        }
    
    vector<int> Ap = dp[0];
    return Ap;
}
```

Los índices en la implementación de `dp` están invertidos con respecto a $f(mask,i)$. Esto es por optimizar el caché del programa y facilitar el obtener el arreglo `Ap` antes del return.

### Solución con memoria lineal
La solución anterior es la mejor solución en tiempo. Sin embargo, utiliza $O(k2^k)$ de memoria. Se puede mejorar la DP para que utilice memoria lineal.

Notemos que $f(mask,i)$ sólo depende de estados con $i-1$ y con $mask$ que sea menor o igual a $mask$, por lo que podemos implementar la DP recursiva de la siguiente forma:

```C++
vector<int> SOS_DP(vector<int> A, int k) {
    vector<int> Ap = A;
    for (int i = k - 1; i >= 0; i--)
    	for (int mk = 0; mk < (1 << k); mk++)
    		if (mk & (1 << i))
    			Ap[mk] += Ap[mk - (1 << i)];
    return Ap;
}
```

La complejidad en tiempo también es $O(k2^k)$, pero la memoria ahora es lineal.

## Inversa de la SOS DP
Ahora queremos resolver el problema inverso.

:::success
:information_source: **Problema 2**
Dado el arreglo $A'=[a'_0, a'_1, \dots, a'_{2^k-1}]$, donde cada elemento cumple la siguiente igualdad:

$$
a'_i=\sum_{s \subseteq i} a_s
$$

Encontrar el arreglo $A$.

**Ejemplo:**
Sea $A'=[4,11,5,12,6,16,8,23]$. Este es el arreglo $A'$ que se obtiene en el ejemplo del problema $1$, así que la respuesta es el arreglo $A=[4,7,1,0,2,3,1,5]$.
:::

### Solución $O(4^k)$ o $O(3^k)$
Obtener el valor de $a_0$ es fácil, pues $a'_0=a_0$. Ahora supóngamos que ya tenemos el valor de $a_i$ para todos los valores de $i<mask$, ¿cómo podemos hallar el valor de $a_{mask}$?

Recordemos que:

$$
a'_i=\sum_{s \subseteq i} a_s =a_i+\sum_{s \subsetneq i} a_s
$$

Despejando $a_i$ tenemos:

$$
a_i=a'_i-\sum_{s \subsetneq i} a_s
$$

La última suma itera sobre valores $s$ que son menores a $i$. Así podemos hallar $a_i$ teniendo los valores de índices menores.

Para implementar la solución, podemos hacer la bruta en $O(4^k)$ o iterar sólo sobre todas las submáscaras para tener una solución en $O(3^k)$. La implementación de esta última sería:

```C++
vector<int> SOS_DP_inv (vector<int> Ap, int k) {
    vector<int> A(1 << k);
   
    for (int mk = 0; mk < (1 << k); mk++) {
	    A[mk] = Ap[mk];
	    for (int s = mk; s != 0;) {
	    	s = (s - 1) & mk;
	        A[mk] -= A[s];
	    }
	}
    
    return A;
}
```

### Solución $O(k2^k)$
Ahora veamos cómo invertir la DP usada anteriormente. Recordemos que $f(mask,k)=a_{mask}$ y que $f(mask,0)=a'_{mask}$. Podemos recuperar fácilmente el valor de $f(mask,0)$ y queremos obtener el valor de $f(mask,k)$. ¿Cómo recuperamos $f(mask,k)$? Vamos a reconstruir todos los valores de la DP a partir de $f(mask,0)$.

Esto lo vamos a hacer desde $i=0$ hasta $i=k$. Si el bit $i$ no está prendido en $mask$, observemos que simplemente $f(mask,i+1)=f(mask,i)$. Y cuando está prendido teníamos $f(mask,i)=f(mask,i+1)-f(mask - 2^i,i+1)$; como queremos hallar $f(mask,i+1)$, lo despejamos en la igualdad anterior: $f(mask,i+1)=f(mask,i)-f(mask - 2^i,i+1)$. Observemos que $f(mask,i+1)$ depende de estados con $i$ menor o de estados con $mask$ menor, por lo que si procesamos los estados con $i$ de menor a mayor y $mask$ de menor a mayor, podemos recuperar todos los estados de la DP.

La implementación quedaría así:

```C++
vector<int> SOS_DP_inv (vector<int> Ap, int k) {
    vector<vector<int>> dp(k + 1, vector<int>(1 << k));
    dp[0] = Ap;
    
    for (int i = 0; i < k; i++)
        for (int mk = 0; mk < (1 << k); mk++)
            if (mk & (1 << i)) 
                dp[i + 1][mk] = dp[i][mk] - dp[i + 1][mk - (1 << i)];
            else
                dp[i + 1][mk] = dp[i][mk];
    
    vector<int> A = dp[k];
    return A;
}
```

### Solución con memoria constante
Podemos ahorrar memoria y utilizar una cantidad lineal. Esto se logra con la misma idea con la que se optimizó la memoria de la SOS DP:

```C++
vector<int> SOS_DP_inv (vector<int> Ap, int k) {
    vector<int> A = Ap;
    for (int i = 0; i < k; i++)
        for (int mk = 0; mk < (1 << k); mk++)
            if (mk & (1 << i)) 
                A[mk] -= A[mk - (1 << i)];
    return A;
}
```

## SOS DP para superconjuntos
:::success
:information_source: **Problema 3**
Dado un arreglo $A=[a_0, a_1, \dots, a_{2^k-1}]$, para cada $i$ tal que $0 \leq i < 2^k$, hallar la suma de las supermáscaras de $i$. Dicho de otra forma, queremos obtener un arreglo $A'=[a'_0, a'_1, \dots, a'_{2^k-1}]$, tal que:

$$
a'_i=\sum_{s \supseteq i} a_s
$$

**Ejemplo:**
Sea $A=[4,7,1,0,2,3,1,5]$. Entonces los elementos de $A'$ serían:

$a'_0=a_0+a_1+a_2+a_3+a_4+a_5+a_6+a_7=4+7+1+0+2+3+1+5=23$
$a'_1=a_1+a_3+a_5+a_7=7+0+3+5=15$
$a'_2=a_2+a_3+a_6+a_7=1+0+1+5=7$
$a'_3=a_3+a_7=0+5=5$
$a'_4=a_4+a_5+a_6+a_7=2+3+1+5=11$
$a'_5=a_5+a_7=3+5=8$
$a'_6=a_6+a_7=1+5=6$
$a'_7=a_7=5=5$

La respuesta debería ser el arreglo $A'=[23,15,7,5,11,8,6,5]$.
:::

Las soluciones $O(4^k)$ y $O(3^k)$ son muy parecidas a las de SOS DP para submáscaras, entonces nos las vamos a saltar.

### Solución $O(k2^k)$
Utilicemos una solución DP parecida a la que usamos en la SOS DP de subconjuntos. Definamos a $f(mask,i)$ como la suma de todas las supermáscaras de $mask$ que tienen los primers $i$ bits idénticos a $mask$. La función $f(mask,i)$ se puede definir como:

$$
f(mask,i) = 
\begin{cases}
a[mask] & \text{si $i = k$} \\
f(mask,i+1)+f(mask + 2^i,i+1) & \text{si $i \notin mask$} \\
f(mask,i+1) & \text{en caso contrario}
\end{cases}
$$

La implementación es muy parecida la DP anterior:

```C++
vector<int> SUPER_SOS_DP(vector<int> A, int k) {
    vector<vector<int>> dp(k + 1, vector<int>(1 << k));
    
    for (int mk = 0; mk < (1 << k); mk++)
        dp[k][mk] = A[mk];
    
    for (int i = k - 1; i >= 0; i--)
        for (int mk = 0; mk < (1 << k); mk++) {
            if (~mk & (1 << i)) 
                dp[i][mk] = dp[i + 1][mk] + dp[i + 1][mk + (1 << i)];
            else
                dp[i][mk] = dp[i + 1][mk];
        }
    
    vector<int> Ap = dp[0];
    return Ap;
}
```

El análisis de complejidad es similar: tiempo y memoria $O(k2^k)$

### Solución con ahorro de memoria
Esta DP también depende de los estados con $i-1$, pero ahora depende de máscaras mayores o iguales a $mask$, por lo que la implementación sería:

```C++
vector<int> SUPER_SOS_DP(vector<int> A, int k) {
    vector<int> Ap = A;
    for (int i = k - 1; i >= 0; i--)
    	for (int mk = 0; mk < (1 << k); mk++)
    		if (~mk & (1 << i))
    			Ap[mk] += Ap[mk + (1 << i)];
    return Ap;
}
```

## Inversa de la SOS DP para superconjuntos
:::success
:information_source: **Problema 4**
Dado el arreglo $A'=[a'_0, a'_1, \dots, a'_{2^k-1}]$, donde cada elemento cumple la siguiente igualdad:

$$
a'_i=\sum_{s \supseteq i} a_s
$$

Encontrar el arreglo $A$.

**Ejemplo:**
Sea $A'=[23,15,7,5,11,8,6,5]$. Este es el arreglo $A'$ que se obtiene en el ejemplo del problema $3$, así que la respuesta es el arreglo $A=[4,7,1,0,2,3,1,5]$.
:::

### Solución $O(4^k)$ o $O(3^k)$
Ahora tenemos que $a_{2^k-1}=a'_{2^k-1}$, ya que $2^k-1$ es la única supermáscara de $2^k-1$. Para hallar todas las $a_i$, observemos que 

$$
a_i=a'_i-\sum_{s \supsetneq i} a_s
$$

Los valores de $s$ por los que itera la suma son mayores a $i$. Juntando las observaciones hechas, llegamos a la conclusión de que podemos hallar los valores de $a_i$ desde los índices más grandes a los más pequeños.

Iterar sobre las supermáscaras de un número es más díficil que iterar sobre las submáscaras. Por lo que ahora iteramos sobre las máscaras de mayor a menor, y cada máscara resta su valor de sus submáscaras:

```C++
vector<int> SUPER_SOS_DP_inv (vector<int> Ap, int k) {
    vector<int> A(1 << k);
   
    for (int mk = (1 << k) - 1; mk >= 0; mk--) {
	    A[mk] += Ap[mk];
	    for (int s = mk; s != 0;) {
	    	s = (s - 1) & mk;
	        A[s] -= A[mk];
	    }
	}
    
    return A;
}
```

La complejidad es $O(3^k)$.

### Solución $O(k2^k)$
Usemos las siguientes observaciones sobre la función $f(mask,i)$ utilizada en SOS para suberconjuntos para llegar a una solución parecida a la inversa en SOS DP de subconjuntos:

- $f(mask,0)=a'_{mask}$
- $f(mask,k)=a_{mask}$
- Si el bit $i$ está prendido en $mask$, entonces $f(mask,i+1)=f(mask,i)$.
- Si el bit $i$ no está prendido en $mask$, entonces $f(mask,i)=f(mask,i+1)+f(mask - 2^i,i+1)$.

Un estado depende de estados con $i$ menor o con $mask$ mayor, así que la implementación se puede hacer de la siguiente forma:

```C++
vector<int> SUPER_SOS_DP_inv (vector<int> Ap, int k) {
    vector<vector<int>> dp(k + 1, vector<int>(1 << k));
    dp[0] = Ap;
    
    for (int i = 0; i < k; i++)
    	for (int mk = (1 << k) - 1; mk >= 0; mk--)
            if (~mk & (1 << i)) 
                dp[i + 1][mk] = dp[i][mk] - dp[i + 1][mk + (1 << i)];
            else
                dp[i + 1][mk] = dp[i][mk];
    
    vector<int> A = dp[k];
    return A;
}
```

### Solución con memoria constante
Para esta inversa también podemos utilizar memoria lineal:

```C++
vector<int> SUPER_SOS_DP_inv (vector<int> Ap, int k) {
    vector<int> A = Ap;
    for (int i = 0; i < k; i++)
    	for (int mk = (1 << k) - 1; mk >= 0; mk--)
            if (~mk & (1 << i)) 
                A[mk] -= A[mk + (1 << i)];
    return A;
}
```

## SOS DP para el complemento
:::success
:information_source: **Problema 5**
Dado un arreglo $A=[a_0, a_1, \dots, a_{2^k-1}]$, para cada $i$ tal que $0 \leq i < 2^k$, hallar la suma de las posiciones cuyo índice no tiene bits en común con $i$. Dicho de otra forma, queremos obtener un arreglo $A'=[a'_0, a'_1, \dots, a'_{2^k-1}]$, tal que:

$$
a'_i=\sum_{s \cap i = 0} a_s
$$

**Ejemplo:**
Sea $A=[4,7,1,0,2,3,1,5]$. Entonces los elementos de $A'$ serían:

$a'_0=a_0+a_1+a_2+a_3+a_4+a_5+a_6+a_7=4+7+1+0+2+3+1+5=23$
$a'_1=a_0+a_2+a_4+a_6=4+1+2+1=8$
$a'_2=a_0+a_1+a_4+a_5=4+7+2+3=16$
$a'_3=a_0+a_4=4+2=6$
$a'_4=a_0+a_1+a_2+a_3=4+7+1+0=12$
$a'_5=a_0+a_2=4+1=5$
$a'_6=a_0+a_1=4+7=11$
$a'_7=a_0=4$

La respuesta debería ser el arreglo $A'=[23,8,16,6,12,5,11,4]$.
:::

La condición de que $i$ y $s$ no compartan bits, es que todos los bits que tenga prendidos $s$ los tenga apagados $i$, y los que tenga prendidos $i$ los renga apagados $s$. Esto es equivalente a decir que $s$ sea una submáscara del complemento de $i$. Por lo que la suma que quieremos hallar es:

$$
a'_i=\sum_{s \subseteq i^C} a_s
$$

Para hallar $A'$, podemos hacer SOS DP en $A$, y asignar $a_i$ al indíce que sea complemento, es decir, $i\oplus((1 << k) - 1)$.

## Aplicación: convoluciones a nivel de bits
### Convolución OR
:::success
:information_source: **Problema 6**
Dado dos arreglos de tamaño $2^k$ $A=[a_0, a_1, \dots, a_{2^k-1}]$ y $B=[b_0, b_1, \dots, b_{2^k-1}]$, queremos calcular otro arreglo $C=[c_0, c_1, \dots, c_{2^k-1}]$, donde el elemento $c_i$ es la suma de los productos de parejas de elementos de $A$ y $B$, tal que el OR a nivel de bits de sus índices es igual a $i$. Formalmente:

$$
c_i=\sum_{j|l=i}a_j \cdot b_l
$$
:::

#### Solución bruta
La solución bruta sería iterar sobre todas las parejas de elementos de $A$ y $B$:

```C++
for (int i = 0; i < (1 << k); i++)
    for (int j = 0; j < (1 << k); j++)
        C[i|j] += A[i] * B[j];
```

#### Solución con SOS DP
Digamos que obtenemos la SOS DP de $A$ y $B$, a las que llamaremos $A'$ y $B'$, es decir, $A'=sos\_dp(A, k)$ y $B'=sos\_dp(B, k)$.

Ahora calculemos el producto punto a punto de $A'$ y $B'$ al cual llamaremos $C'$, es decir, $C'=[a'_0b'_0, a'_1b'_1, \dots, a'_{2^k-1}b'_{2^k-1}]$.

¿Qué significado tiene $c'_i$? Tenemos $c'_i=a'_i \cdot b'_i$, donde $a'_i$ es la suma de todas las submáscaras de $i$ en $A$, y $b'_i$ es la suma de todas las submáscaras de $i$ en $B$, por lo tanto, $c'_i$ es la suma de los productos de parejas $a_j$ y $b_l$, donde tanto $j$ como $l$ son submáscaras de $i$; como ambas son submáscaras de $i$, entonces su OR ($j|l$), también es submáscara de $i$. Esto quiere decir que $c'_i$ es la suma de todas las $c_j$ donde $j$ es submáscara de $i$.

:::spoiler Demostración
$$
\begin{align}
c'_i &= a'_ib'_i \\
    &= (\sum_{j \subseteq i}a_j) \cdot (\sum_{l \subseteq i}b_l) \\
    &= \sum_{j,l \subseteq i} a_j b_l \\
    &= \sum_{j|l \subseteq i} a_j b_l \text{ (ya que $j,l \subseteq i$ es equivalente a $j|l \subseteq i$)} \\
    &= \sum_{s \subseteq i} \sum_{j|l=s}a_jb_l \\
    &= \sum_{s \subseteq i} c_s \\ 
\end{align}
$$
:::

Después de entender el significado que tiene $c'_i$, podemos ver que sólo basta hacerle la SOS DP inversa para obtener $c_i$. El código sería:

```C++
Ap = SOS_DP(A, k);
Bp = SOS_DP(B, k);

for (int i = 0; i < (1 << k); i++)
    Cp[i] = Ap[i] * Bp[i];

C = SOS_DP_inv(Cp, k);
```

Complejidad: $O(k2^k)$

### Convolución AND
:::success
:information_source: **Problema 7**
Dado dos arreglos de tamaño $2^k$ $A=[a_0, a_1, \dots, a_{2^k-1}]$ y $B=[b_0, b_1, \dots, b_{2^k-1}]$, queremos calcular otro arreglo $C=[c_0, c_1, \dots, c_{2^k-1}]$, donde el elemento $c_i$ es la suma de los productos de parejas de elementos de $A$ y $B$, tal que el AND a nivel de bits de sus índices es igual a $i$. Formalmente:

$$
c_i=\sum_{j \& l=i}a_j \cdot b_l
$$
:::

#### Solución con SOS DP
Ahora aprovechemos que la condición $j \& l \supseteq i$ es equivalente a que $j \& l$ sea supermáscara de $i$. Podemos modificar la solución anterior y utilizar la SOS DP para superconjuntos.

:::spoiler Demostración
$$
\begin{align}
c'_i &= a'_ib'_i \\
    &= (\sum_{j \supseteq i}a_j) \cdot (\sum_{l \supseteq i}b_l) \\
    &= \sum_{j,l \supseteq i} a_j b_l \\
    &= \sum_{j \& l \supseteq i} a_j b_l \text{ (ya que $j,l \supseteq i$ es equivalente a $j \& l \supseteq i$)} \\
    &= \sum_{s \supseteq i} \sum_{j \& l=s}a_jb_l \\
    &= \sum_{s \supseteq i} c_s \\ 
\end{align}
$$
:::

```C++
Ap = SUPER_SOS_DP(A, k);
Bp = SUPER_SOS_DP(B, k);

for (int i = 0; i < (1 << k); i++)
    Cp[i] = Ap[i] * Bp[i];

C = SUPER_SOS_DP_inv(Cp, k);
```

Complejidad: $O(k2^k)$

## Lectura recomendada
### Explicaciones alternativas de SOS DP
- https://codeforces.com/blog/entry/45223
- https://usaco.guide/adv/dp-sos
- https://codeforces.com/blog/entry/105247

### Convolución OR y otras convoluciones
- https://codeforces.com/blog/entry/115438 (recomendado leer también los comentarios para ver cómo se puede generalizar la idea a más convoluciones)

### Convolución XOR explicado con FFT
- https://hackmd.io/@8iUL97mRS2mgfTEJDy5xmA/H1EYLooPC

## Problemas
- https://cses.fi/problemset/task/1654
- https://codeforces.com/contest/165/problem/E
- https://codeforces.com/contest/383/problem/E
- https://usaco.org/index.php?page=viewproblem2&cpid=1309
- https://codeforces.com/contest/1208/problem/F