BZOJ4328 JSOI2012始祖鸟【异或方程组】

本文介绍了一个有趣的算法问题——始祖鸟聚会。问题要求合理分配始祖鸟到两个不同的聚会地点,使得每个地点内的始祖鸟认识的朋友数量均为偶数。通过使用异或方程组和高斯消元法,文章详细解释了解决方案,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


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]xorA[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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值