传送门
PROBLEM
现在有N只始祖鸟,我们从1开始编号。对于第i只始祖鸟,有Mi个认识的朋友,它们的编号分别是Fi,1,Fi,2,…,Fi,Mi。朋友的认识关系是单向的,也就是说如果第s只始祖鸟认识第t只始祖鸟,那么第t只始祖鸟不一定认识第s只始祖鸟。
聚会的地点分为两处,一处在上游,一处在下游。对于每一处聚会场所,都必须满足对于在这个聚会场所中的始祖鸟,有恰好有偶数个自己认识的朋友与之在同一个聚会场所中。当然,每一只始祖鸟都必须在两处聚会场所之一。
现在需要你给出一种安排方式。你只需要给出在上游的始祖鸟编号,如果有多组解,请输出任何一组解。
INPUT
输入数据包含N+1行,第一行是数字N,代表始祖鸟的个数。 之后的N行,第i+1行的第一个数字是M[i],表示第i只鸟的朋友个数。之后有M[i]个数字依次为 F[i][1],F[i][2],…,F[i][M[i]]表示第i只始祖鸟朋友的标号。
OUTPUT
输出数据包含2行,第一行有一个非负整数k,表示在上游参加聚会的始祖鸟个数。第二行有k个正整数,表示在这个k只始祖鸟的编号,你可以以任意顺序输出这些编号。如果无法满足要求,只输出一行“Impossible”。
EXAMPLES
in.
5
3 2 3 4
2 1 3
4 2 1 4 5
2 1 3
1 3
out.
3
1 2 3
SOL
发现本质在于选与不选,用异或方程组解决(高斯消元+bitset优化)
A
[
i
]
[
1
]
∗
x
[
1
]
x
o
r
A
[
i
]
[
2
]
∗
x
[
2
]
x
o
r
…
A
[
i
]
[
n
]
∗
x
[
n
]
=
0
A[i][1]*x[1] xor A[i][2]*x[2]xor\dots A[i][n]*x[n]=0
A[i][1]∗x[1]xorA[i][2]∗x[2]xor…A[i][n]∗x[n]=0
(1:上游 0:下游)
这是有两个问题:
1.这样只能让上游的成立,下游的不清楚
(有想过重新定义xor,即 1,2,0三个,发现不满足结合律,没法消元。。。)
2.还要考虑当前点放在 上游 或 下游 (如果下游那么这个方程就无意义)
学习了大佬的博客
找到了解决办法:
在原来的方程上,
分类讨论当前点选择上游或下游的成立条件:
如果当前出边为偶数: 无论上游还是下游,都要有偶数个朋友选择上游
如果当前出边为奇数,若当前为1,要偶数个朋友 为1 若当前为0 要奇数个朋友为1
这样稍微改一下系数就OK了
反思:
异或方程组的更改要思考 如何相互影响
(此题是通过出边奇偶性决定补集情况)(不是改异或。。。)
CODE
#include<bits/stdc++.h>
#define pf printf
#define sf scanf
using namespace std;
const int maxn=2e3+10;
bitset<maxn> a[maxn];
int ans[maxn],amt=0,n;
inline void gauss(){
for(int k=1;k<=n;++k){
bool f=0;
for(int i=k;i<=n;++i)if(a[i][k]){swap(a[i],a[k]);f=1;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]){
puts("Impossible");
exit(0);
}
}
}
signed main (){
sf("%d",&n);
for(int i=1;i<=n;++i){
int m;sf("%d",&m);
if(m&1)a[i][i]=a[i][n+1]=1;
while(m--){
int f;sf("%d",&f);a[i][f]=1;
}
}
gauss();
cout<<amt<<'\n';
for(int i=1;i<=n;++i)if(ans[i])cout<<i<<' ';
return 0;
}