P9333 [JOISC 2023 Day2] Council

发布时间 2023-12-28 22:00:29作者: Chy12321

钦定议长后,决定副议长时每个议案的赞成票数变动幅度最大为 \(1\),所以只有赞成票数为 \(\lfloor \dfrac n 2 \rfloor\) 的议案的通过与否会发生变化。

设第 \(i\) 名议员投反对票的议案集合为 \(T_i\),钦定议长为 \(i\) 后可能发生变化的议案集合为 \(S_i\),则问题转化为求 \(\max\limits_{ j \ne i} |S_i \cap T_j|\)

显然 \(T_j \supseteq (S_i \cap T_j), (S_i \cap T_j) \subseteq S_i\),所以由 \(T_j\) 推到 \(S_i\) 先做一遍超集和,再做一遍子集和即可(可参考 SOSDP)。

\(f(S)\) 表示能使 \(|S \cap T_j|\) 取得最大值的 \(j\),注意到 \(f(S_i) \ne i\),所以要维护两个(即最大值和次大值对应的) \(j\)

时间复杂度 \(\mathcal O(nm + m2^m)\)

代码:

#include <bits/stdc++.h>

using namespace std;

constexpr int N = 3e5 + 10, M = 20;

int n, m, a[N][M + 1], c[M + 1], T[N], f[1 << M][2];
int S0, S1, cnt;

int main() {
    ios_base::sync_with_stdio(0); cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < m; j++) cin >> a[i][j], T[i] = (T[i] << 1) ^ a[i][j] ^ 1, c[j] += a[i][j];
        f[T[i]][0] ? f[T[i]][1] = i : f[T[i]][0] = i;
    }
    for (int i = 0; i < m; i++) {
        if (c[m - i - 1] == n / 2) S0 ^= (1 << i);
        else if (c[m - i - 1] == n / 2 + 1) S1 ^= (1 << i);
        else cnt += (c[m - i - 1] > n / 2);
    }
    
    for (int i = 0; i < m; i++) {
        for (int S = 0; S < (1 << m); S++) {
            if (!(S & (1 << i))) {
                int *from = f[S ^ (1 << i)], *to = f[S];
                if (!to[0]) to[0] = from[0], to[1] = from[1];
                else if (!to[1]) to[1] = from[0] == to[0] ? from[1] : from[0];
            }
        }
    }
    
    vector<int> w;
    for (int i = 0; i < m; i++) {
        for (int S = 0; S < (1 << m); S++) {
            if (S & (1 << i)) {
                w.clear();
                int *from = f[S ^ (1 << i)], *to = f[S];
                for (int i : {0, 1}) w.emplace_back(to[i]), w.emplace_back(from[i]);
                sort(w.begin(), w.end(), [&](int a, int b) {return __builtin_popcount(S & T[a]) > __builtin_popcount(S & T[b]);});
                w.erase(unique(w.begin(), w.end()), w.end());
                to[0] = w[0]; if (w.size() > 1) to[1] = w[1];
            }
        }
    }

    for (int i = 1; i <= n; i++) {
        int S = (S0 & T[i]) | (S1 & (~T[i]));
        cout << cnt + __builtin_popcount(S1 & T[i]) + __builtin_popcount(S & T[f[S][0] == i ? f[S][1] : f[S][0]]) << '\n';
    }
    return 0;
}