题解 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\) 表示上游。
这样可能会出现两个问题:
- 只能让上游的成立,下游的不清楚。(试想过重新定义 \(\operatorname{xor}\),即 \(1\),\(2\),\(0\) 三个,发现不满足结合律,没有办法消元,所以此办法行不通。)
- 要考虑当前点放在上游或下游(如果下游那么这个方程就无意义)。
稍加思考,我们就可以找到解决办法:在原来的方程上,分类讨论当前点选择上游或下游的成立条件。如果当前出边为偶数,无论上游还是下游,都要有偶数个朋友选择上游。如果当前出边为奇数,若当前为 \(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;
}

浙公网安备 33010602011771号