题解:P3429 [POI 2005] DWA-Two Parties

【题目传送门】

第一次独立做出来紫诶。


看到这种题下意识想到高斯消元。

\(x_i\) 表示第 \(i\) 个人的选择,\(x_i\) 只能是 \(0\)\(1\) 之一,即前往哪个派对。对于每个人,ta 会对答案产生贡献当且仅当其同派对邻居数量为偶数。因此,我们可以将问题转换为 \(\mathrm{GF(2)}\) 上的问题。

对于一个关系 \((i,j)\),当且仅当 \(x_i=x_j\)\(j\) 才会影响 \(i\)。同时一个 \(j\) 影响 \(i\),也必需让 \((i,j)\) 这个关系存在。所以只有以上两个条件都满足的时候,\(j\) 才会影响 \(i\)。故我们可以再设 \(a_{i,j}\) 表示 \(i\)\(j\) 是否存在关系。由于两者要同时满足,所以一个人 \(j\)\(a_{i,j}[x_j = x_i] = 1\) 时才影响 \(i\),而易发现 \([x_j = x_i]=1\oplus x_i\oplus x_j\)。根据题目要求,影响第 \(i\) 个人的人数为偶数时 \(i\) 才会对答案产生贡献,即:

\[\bigoplus_{j=1}^Na_{i,j}(1\oplus x_i\oplus x_j)=0 \]

\(i\) 会计入答案。

\(d_i\) 表示与 \(i\) 有关系的人数个数,\(c_i = d_i\bmod2\),展开上面的式子:

\[c_i \oplus (c_ix_i)\oplus\bigoplus_{j=1}^Na_{i,j}x_j=0 \]

移项后可得

\[(c_ix_i)\oplus\bigoplus_{j=1}^Na_{i,j}x_j=c_i \]

于是对于每个 \(i\),我们都得到了这样一个方程,组合到一起就变成了在 \(\mathrm{GF(2)}\) 上的线性方程。

最终我们希望选择一组 \(x_i\),使得尽可能多的方程成立,并且

定理(Gallai 环-余环划分定理):对于任意无向图,总存在一种将顶点划分为两个集合的方案,使得每个顶点在自己所属集合内部的邻居数量为偶数。

根据该定理,存在一种划分使所有顶点在自己所属集合中的邻居数均为偶数。将这两个集合分别记为 \(x_i=0\)\(x_i=1\),即可得到上述方程组的一组解。因此方程组一定有解,可以直接进行高斯消元。

最终根据求解出的 \(x_i\) 分配方案输出即可,由于题目要求任意合法方案都没问题,所以我们令最终的自由元为 \(0\) 即可。


AC 代码见下。

#include<bits/stdc++.h>
#define TESTING 0
#define ift if (TESTING)
#define ft first
#define sd second
#define pb push_back
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define int long long
#define qwq while (1) cout << "qwq" << "\n"
#define debug(x) cout << #x << "=" << x << "\n"
#define pii pair<int, int>
using namespace std;
const int N = 200 + 5, INF = 0x3f3f3f3f;
int n, x[N];
bitset<N> a[N];
void Gauss() {
    int row = 1;
    for (int col = 1; col <= n; col++) {
        int p = row;
        while (p <= n && !a[p][col]) p++;
        if (p > n) continue;
        swap(a[p], a[row]);
        for (int i = 1; i <= n; i++) {
            if (i != row && a[i][col])
                a[i] ^= a[row];
        }
        row++;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (a[i][j]) {
                x[j] = a[i][n + 1];
                break;
            }
        }
    }
}
auto init() -> void{}
auto solution() -> void{
    cin >> n;
    rep(i, 1, n) {
        int d;
        cin >> d;
        a[i][n + 1] = d & 1;
        if (d & 1) a[i][i] = 1;
        rep(j, 1, d) {
            int v;
            cin >> v;
            a[i][v] = 1;
        }
    }
    Gauss();
    vector<int> ans;
    rep(i, 1, n) {
        if (x[i]) ans.pb(i);
    }
    cout << ans.size() << '\n';
    for (auto v : ans) cout << v << ' ';
    cout << '\n';
}
auto main() -> signed{
    ios :: sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T--){
        init();
        solution();
    }
    return 0;
}
posted @ 2026-09-08 14:47  PigeonTree  阅读(2)  评论(0)    收藏  举报