题解 Luogu P6126 & AcWing.2396 [JSOI2012] 始祖鸟

大致思考题意,很容易发现此题使用异或方程组解决,本质上就是一个高斯消元的操作。为了优化,我们可以使用 bitset(不熟悉 bitset 的可以去这里)。

\(A[i][1] \times x[1]\) \(\operatorname{xor}\) \(A[i][2] \times x[2]\) \(\operatorname{xor}\) \(...A[i][n] \times x[n] = 0\),其中 \(0\) 表示下游,\(1\) 表示上游。

这样可能会出现两个问题:

  1. 只能让上游的成立,下游的不清楚。(试想过重新定义 \(\operatorname{xor}\),即 \(1\)\(2\)\(0\) 三个,发现不满足结合律,没有办法消元,所以此办法行不通。)
  2. 要考虑当前点放在上游或下游(如果下游那么这个方程就无意义)。

稍加思考,我们就可以找到解决办法:在原来的方程上,分类讨论当前点选择上游或下游的成立条件。如果当前出边为偶数,无论上游还是下游,都要有偶数个朋友选择上游。如果当前出边为奇数,若当前为 \(1\),要偶数个朋友为 \(1\);若当前为 \(0\),要奇数个朋友为 \(1\)。这样将原来的方程稍微修改一下系数即可。

#include <iostream>
#include <bitset>
#include <cmath>
using namespace std;

const int maxn = 2e3 + 10;
bitset<maxn> a[maxn];
int ans[maxn], amt = 0, n;

void work()
{
    for (int k = 1; k <= n; k++)
    {
        bool f = false;
        for (int i = k; i <= n; i++)
            if (a[i][k])
            {
                swap(a[i], a[k]);
                f = true;
                break;
            }
        if (!f) continue;

        for (int i = 1; i <= n; i++)
        {
            if (a[i][k] && k != i) a[i] ^= a[k];
        }
    }
    for (int i = n; i > 0; i--)
    {
        if (a[i][i])
        {
            ans[i] = a[i][n + 1];
            amt += ans[i];
        }
        else if (a[i][n + 1])
        {
            cout << "Impossible" << endl;
            exit(0);
        }
    }
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int m;
        cin >> m;
        if (m & 1) a[i][i] = a[i][n + 1] = 1;
        while (m--)
        {
            int f;
            cin >> f;
            a[i][f] = 1;
        }
    }
    work();
    cout << amt << endl;

    for (int i = 1; i <= n; i++)
    {
        if (ans[i]) cout << i << " ";
    }
    cout << endl;
    return 0;
}
posted @ 2022-04-06 10:36  wbs200  阅读(107)  评论(0)    收藏  举报