Гайдо́ра чи не Гайдо́ра?#12298

Гайдо́ра чи не Гайдо́ра? (Розбір)

Автор задачі:

Хайдара Аль Макдід

Підготовлено:

Хайдара Аль Макдід

Редагував:

Хасан Салех (MathModel)

Розв'язок

Ми хочемо максимізувати значення

a_1 \,\&\, a_2 \,\&\, \dots \,\&\, a_n

після вибору точно різних індексів і заміни кожного вибраного a_i на його 32-бітне побітове заперечення \sim a_i. Оскільки береться над 32 бітами, ми можемо розглядати це як \sim x = (2^{32}-1) - x, або, еквівалентно, \sim x = (\texttt{MASK} \,\hat{}\, x) з \texttt{MASK} = 2^{32}-1.

Нехай остаточна відповідь буде . Для кожного біта, який дорівнює в , кожен елемент у фінальному масиві також повинен мати цей біт рівним , інакше AND матиме у цій позиції. Це підказує про побудову жадібно біт за бітом, починаючи з найбільш значущого біта до найменш значущого: ми підтримуємо поточну відповідь ans, і для кожного біта з до ми намагаємося встановити його в поверх того, що ми вже зафіксували.

Конкретно, на біті ми формуємо кандидатне значення

\texttt{cand} = \texttt{ans} \,\vert\, (1 \ll j),

що означає, що ми хочемо, щоб фінальний AND мав усі біти, які дорівнюють у cand. Для цього зафіксованого cand ми проводимо перевірку здійсненності на всьому масиві. Для кожного елемента x = a_i є лише два стани у фінальній конфігурації: або ми зберігаємо його як , або ми перевертаємо його на y = \sim x = (\texttt{MASK} - x). Щоб cand було досяжним, для кожної позиції принаймні один з цих варіантів повинен повністю покривати всі біти cand:

\text{keep\_ok} \iff (x \,\&\, \texttt{cand}) = \texttt{cand}, \qquad \text{flip\_ok} \iff (y \,\&\, \texttt{cand}) = \texttt{cand}.

Дивлячись на (\text{keep\_ok}, \text{flip\_ok}), ми ділимо індекси на три корисні типи:

  • Якщо обидва є хибними, ні , ні не мають усіх бітів cand, тому цей індекс ніколи не може задовольнити cand. Тоді cand сама по собі є неможливою, і ми повинні відхилити цей біт.

  • Якщо \text{keep\_ok} = \text{false} і \text{flip\_ok} = \text{true}, ми змушені перевернути цей індекс, щоб задовольнити cand. Нехай буде кількість таких змушених індексів.

  • Якщо \text{keep\_ok} = \text{true} і \text{flip\_ok} = \text{true}, цей індекс є гнучким: як збереження, так і перевертання все ще задовольняють cand. Нехай буде кількість таких гнучких індексів.

(Коли \text{keep\_ok} = \text{true} і \text{flip\_ok} = \text{false}, ми просто повинні зберегти елемент; це не споживає жодного перевертання і не дає жодної гнучкості.)

Ми повинні виконати точно перевертів загалом. Для фіксованого cand усі змушені індекси повинні бути перевернуті, тому нам потрібно принаймні перевертів. З іншого боку, ми можемо за бажанням перевернути будь-яку підмножину з гнучких індексів, тому загальна кількість перевертів, які ми можемо реалізувати, залишаючи cand задовольняти, коливається від (тільки змушені) до F+C (змушені плюс усі гнучкі). Отже, cand є здійсненним, якщо і тільки якщо

F \le k \le F + C.

Якщо ця нерівність виконується, ми знаємо, що існує спосіб вибрати точно індексів для перевертання так, щоб кожен фінальний елемент покривав усі біти cand. У цьому випадку ми приймаємо цей біт і встановлюємо \texttt{ans} := \texttt{cand}. Якщо ні, ми залишаємо біт на в ans і переходимо до наступного нижчого біта.

Оскільки ми обробляємо біти з найбільш значущого вниз і встановлюємо біт лише тоді, коли існує якась конфігурація з точно перевертаннями, яка підтримує всі вибрані біти до цього моменту, отримане ans є лексикографічно (а отже, чисельно) максимальним фінальним AND, який можна досягти. Для кожної з позицій біта ми скануємо всі елементів один раз, тому загальна складність становить O(32n) = O(n) за часом і додаткової пам'яті, крім масиву вхідних даних.

n,k=map(int, input().split())
a=list(map(int, input().split()))

MASK=(1<<32)-1
ans=0

for j in range(31, -1, -1):
    p=1<<j
    cand = ans | p
    forced = 0
    flex = 0
    ok = True
    for x in a:
        keep = (x & cand) == cand
        flipped = (~x) & MASK     
        flip = (flipped & cand) == cand

        if not keep and not flip:
            ok = False
            break
        if not keep and flip:
            forced += 1       
        elif keep and flip:
            flex += 1         

    if not ok:
        continue

    rem = k - forced
    if rem < 0 or rem > flex:
        continue

    ans = cand

print(ans)