Подарунок#12321

Подарунок (Розбір)

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

Андрій Холод (Connect)

Підготував:

Андрій Холод (Connect)

Аналіз:

Андрій Холод (Connect)

Розв’язок

1) Як описати всі операції змінними

Нехай r_i \in \{0,1\} — чи інвертуємо рядок , а c_j \in \{0,1\} — чи інвертуємо стовпець . Тоді після всіх операцій елемент стане: [ A'_{i,j} = A_{i,j} \oplus r_i \oplus c_j ]

Умова гарності: [ A_{i,j} \oplus r_i \oplus c_j \;=\; A_{i',j'} \oplus r_{i'} \oplus c_{j'} ] де i' = n-i+1, j' = m-j+1.

Переносимо відоме в одну сторону: [ (r_i \oplus r_{i'}) \oplus (c_j \oplus c_{j'}) = A_{i,j} \oplus A_{i',j'} ]

2) Зводимо до змінних лише для пар

Розіб’ємо рядки на пари (i, i') і стовпці на пари (j, j'). Кількість пар: [ R=\lfloor n/2 \rfloor,\quad C=\lfloor m/2 \rfloor ]

Введемо нові змінні: [ R_i = r_i \oplus r_{i'},\quad C_j = c_j \oplus c_{j'} ] для i=0..R-1, j=0..C-1.

Тоді для кожної пари рядків і пари стовпців маємо рівняння: [ R_i \oplus C_j = W_{i,j} ] де [ W_{i,j} = A_{i,j} \oplus A_{i',j'} ]

3) Необхідна умова існування (перевірка 2х2)

Для фіксованих і розглянемо 4 клітинки: [ (i,j),\ (i',j'),\ (i,j'),\ (i',j) ] Має виконуватись: [ A_{i,j}\oplus A_{i',j'} = A_{i,j'} \oplus A_{i',j} ] бо обидва вирази дорівнюють R_i \oplus C_j (один раз через (j,j'), інший раз через переставлені стовпці). Якщо для хоча б одного такого блока значення різні — рішення не існує, відповідь .

Саме це робиться в коді:

  • x = A_{i,j} \oplus A_{i',j'}

  • y = A_{i,j'} \oplus A_{i',j}

  • якщо x \ne y — друкуємо

  • інакше W_{i,j} = x

4) Фіксації при непарних або

Якщо непарне, існує центральний рядок . Для нього i=i' і тому r_i \oplus r_{i'}=0. Тоді з умови: [ A'_{mid,j} = A'_{mid,j'} \Rightarrow (c_j \oplus c_{j'}) = A_{mid,j} \oplus A_{mid,j'} ] Тобто для кожної пари стовпців отримуємо фіксоване значення C_j.

Аналогічно, якщо непарне, є центральний стовпець і він фіксує кожен R_i: [ (r_i \oplus r_{i'}) = A_{i,midc} \oplus A_{i',midc} ]

У коді це робиться через setVal і масиви fixedR/fixedC.

5) Графова інтерпретація рівнянь

Маємо систему: [ R_i \oplus C_j = W_{i,j} ] Це — двочастковий граф: вершини зліва — R_i, справа — C_j, а ребро має “вагу” W_{i,j}.

Якщо ми знаємо значення однієї вершини, то з ребра одразу знаємо сусіда: [ C_j = R_i \oplus W_{i,j},\quad R_i = C_j \oplus W_{i,j} ] Отже, по зв’язній компоненті всі значення визначаються однозначно (з точністю до глобального XOR, якщо компонент не зафіксований).

Саме тому розв’язок робить BFS по компонентах і розставляє значення змінним.

6) Мінімізація кількості операцій

Важливо: ми мінімізуємо кількість операцій рядків/стовпців, а не кількість одиниць у матриці.

Як це пов’язано з R_i і C_j?

  • Якщо R_i = 1, то в парі рядків (i,i') потрібно інвертувати рівно один рядок (це коштує 1 операцію). Якщо R_i=0 — або інвертуємо обидва, або жодного (можна зробити 0 операцій у цій парі, бо "обидва" ніколи не вигідно). Тобто мінімальна ціна для пари — це просто R_i.

  • Аналогічно для C_j — ціна пари стовпців дорівнює C_j.

Отже мінімізувати кількість операцій — це мінімізувати: [ \sum_i R_i + \sum_j C_j ]

7) Що робимо у зв’язній компоненті

Під час BFS для компоненти рахуємо:

  • — кількість вершин у компоненті (і -вершин, і -вершин),

  • — скільки з них отримали значення 1,

  • compFixed — чи є у компоненті хоча б одна “зафіксована” вершина (через середній рядок/стовпець).

Якщо компонент зафіксований, то значення вже єдині вартість компоненти = .

Якщо компонент не зафіксований, то можна одночасно інвертувати всі значення в компоненті: [ R_i \leftarrow R_i \oplus 1,\quad C_j \leftarrow C_j \oplus 1 ] і всі рівняння збережуться. Тоді ми можемо вибрати дешевший з двох варіантів: [ \min(ones,\ tot-ones) ]

Сума по всіх компонентах і є відповіддю.

8) Асимптотика

Зберігаємо лише R \cdot C \le 2500 \cdot 2500 = 6.25 \cdot 10^6 значень .

BFS у такому “повному двочастковому графі” реалізований як:

  • з вершини-рядка проходимо по всіх стовпцях;

  • з вершини-стовпця — по всіх рядках.

У найгіршому випадку це O(R \cdot C) переходів, що вкладається в обмеження.

Пам’ять: для зчитування (у коді матриця як рядки) і для .

Код рішення

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define speed ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define endl '\n'
#define pb push_back

static int n, m, R, C;
static vector<string> s;
static vector<vector<int>> W;

static vector<int> valR, valC;
static vector<int> fixedR, fixedC;
static vector<int> visR, visC;

static deque<int> q;
static ll answer;

int bitAt(int i, int j) {
    return s[i][j] - '0';
}

bool setVal(vector<int>& val, vector<int>& fx, int idx, int need) {
    if (val[idx] == -1) {
        val[idx] = need;
        fx[idx] = 1;
        return true;
    }
    return val[idx] == need;
}

bool pushRow(int i, int v) {
    if (valR[i] == -1) valR[i] = v;
    else if (valR[i] != v) return false;
    return true;
}

bool pushCol(int j, int v) {
    if (valC[j] == -1) valC[j] = v;
    else if (valC[j] != v) return false;
    return true;
}

bool bfs_component(int start) {
    bool compFixed = false;
    ll ones = 0, tot = 0;

    q.clear();
    q.push_back(start);

    while (!q.empty()) {
        int node = q.front();
        q.pop_front();

        if (node < R) {
            int i = node;
            if (visR[i]) continue;
            visR[i] = 1;

            tot++;
            if (fixedR[i]) compFixed = true;
            if (valR[i] == 1) ones++;

            int vr = valR[i];
            for (int j = 0; j < C; j++) {
                int needC = vr ^ W[i][j];
                if (!pushCol(j, needC)) return false;
                if (!visC[j]) q.push_back(R + j);
            }
        } else {
            int j = node - R;
            if (visC[j]) continue;
            visC[j] = 1;

            tot++;
            if (fixedC[j]) compFixed = true;
            if (valC[j] == 1) ones++;

            int vc = valC[j];
            for (int i = 0; i < R; i++) {
                int needR = vc ^ W[i][j];
                if (!pushRow(i, needR)) return false;
                if (!visR[i]) q.push_back(i);
            }
        }
    }

    if (compFixed) answer += ones;
    else answer += min(ones, tot - ones);

    return true;
}

void solve() {
    cin >> n >> m;

    s.assign(n, "");
    for (int i = 0; i < n; i++) {
        s[i].reserve(m);
        for (int j = 0; j < m; j++) {
            char c;
            cin >> c;
            s[i].push_back(c);
        }
    }

    R = n / 2;
    C = m / 2;

    W.assign(R, vector<int>(C, 0));

    valR.assign(R, -1);
    valC.assign(C, -1);
    fixedR.assign(R, 0);
    fixedC.assign(C, 0);
    visR.assign(R, 0);
    visC.assign(C, 0);

    answer = 0;

    for (int i = 0; i < R; i++) {
        int i2 = n - 1 - i;
        for (int j = 0; j < C; j++) {
            int j2 = m - 1 - j;

            int a1 = bitAt(i, j);
            int a2 = bitAt(i2, j2);
            int a3 = bitAt(i, j2);
            int a4 = bitAt(i2, j);

            int x = a1 ^ a2;
            int y = a3 ^ a4;

            if (x != y) {
                cout << -1 << endl;
                return;
            }
            W[i][j] = x;
        }
    }

    if (n & 1) {
        int mid = n / 2;
        for (int j = 0; j < C; j++) {
            int j2 = m - 1 - j;
            int need = bitAt(mid, j) ^ bitAt(mid, j2);
            if (!setVal(valC, fixedC, j, need)) {
                cout << -1 << endl;
                return;
            }
        }
    }

    if (m & 1) {
        int midc = m / 2;
        for (int i = 0; i < R; i++) {
            int i2 = n - 1 - i;
            int need = bitAt(i, midc) ^ bitAt(i2, midc);
            if (!setVal(valR, fixedR, i, need)) {
                cout << -1 << endl;
                return;
            }
        }
    }

    for (int i = 0; i < R; i++) {
        if (valR[i] != -1 && !visR[i]) {
            if (!bfs_component(i)) {
                cout << -1 << endl;
                return;
            }
        }
    }
    for (int j = 0; j < C; j++) {
        if (valC[j] != -1 && !visC[j]) {
            if (!bfs_component(R + j)) {
                cout << -1 << endl;
                return;
            }
        }
    }

    for (int i = 0; i < R; i++) {
        if (!visR[i]) {
            if (valR[i] == -1) valR[i] = 0;
            if (!bfs_component(i)) {
                cout << -1 << endl;
                return;
            }
        }
    }
    for (int j = 0; j < C; j++) {
        if (!visC[j]) {
            if (valC[j] == -1) valC[j] = 0;
            if (!bfs_component(R + j)) {
                cout << -1 << endl;
                return;
            }
        }
    }

    cout << answer << endl;
}

signed main() {
    speed;
    int test = 1;
    // cin >> test;
    while (test--) solve();
    return 0;
}