P2447 [SDOI2010] 外星千足虫 题解

@

[SDOI2010] 外星千足虫

题目描述

公元 \(2333\)\(2\)\(3\) 日,在经历了 \(17\) 年零 \(3\) 个月的漫长旅行后,“格纳格鲁一号”载人火箭返回舱终于安全着陆。此枚火箭由美国国家航空航天局(NASA)研制发射,行经火星、金星、土卫六、木卫二、谷神星、“张衡星”等 \(23\) 颗太阳系星球,并最终在小行星“杰森星”探寻到了地外生命。宇航员在“杰森星”地表岩层下 \(45.70\) 米位置发现一批珍贵的活体生命样本,并将其带回检测。

在带回的活体样本中,最吸引人的当属这些来自外星的千足虫了。这些虫子身躯纤长,身体分为若干节。受到触碰时,会将身体卷曲成圆环形,间隔一段时间后才会复原活动。

有趣的还不止如此。研究人员发现,这些虫子的足并不像地球千足虫成对出现、总共偶数条——它们每节身体下方都有着不定数量的足,但足的总数一定是奇数条!

虽然从外观难以区分二者,但通过统计足的数目,科学家们就能根据奇偶性判断出千足虫所属的星球。

作为 J 国派去 NASA 的秘密间谍,你希望参加这次研究活动以掌握进一步的情报,而 NASA 选拔的研究人员都是最优秀的科学家。于是 NASA 局长 Charles Bolden 出了一道难题来检测你的实力:

现在你面前摆有 \(1\ldots N\) 编号的 \(N\) 只千足虫,你的任务是鉴定每只虫子所属的星球,但不允许亲自去数它们的足。

Charles 每次会在这 \(N\) 只千足虫中选定若干只放入“昆虫点足机”(the Insect Feet Counter, IFC)中,“点足机”会自动统计出其内所有昆虫足数之和。Charles 会将这个和数 \(\bmod\) \(2\) 的结果反馈给你,同时告诉你一开始放入机器中的是哪几只虫子。

他的这种统计操作总共进行 \(M\) 次,而你应当尽早得出鉴定结果。

假如在第 \(K\) 次统计结束后,现有数据就足以确定每只虫子的身份,你就还应将这个 \(K\) 反馈给 Charles,此时若 \(K<M\),则表明那后 \(M-K\) 次统计并非必须的。

如果根据所有 \(M\) 次统计数据还是无法确定每只虫子身份,你也要跟 Charles 讲明:就目前数据会存在多个解。

输入格式

第一行是两个正整数 \(N,M\)

接下来 \(M\) 行,按顺序给出 Charles 这 \(M\) 次使用“点足机”的统计结果。每行包含一个 \(01\) 串和一个数字,用一个空格隔开。\(01\) 串按位依次表示每只虫子是否被放入机器:如果第 \(i\) 个字符是 \(0\) 则代表编号为 \(i\) 的虫子未被放入,\(1\) 则代表已被放入。后面跟的数字是统计的昆虫足数 \(\bmod\) \(2\) 的结果。

由于 NASA 的实验机器精确无误,保证前后数据不会自相矛盾。即给定数据一定有解。

输出格式

在给定数据存在唯一解时有 \(N+1\) 行,第一行输出一个不超过 \(M\) 的正整数 \(K\),表明在第 \(K\) 次统计结束后就可以确定唯一解;接下来N行依次回答每只千足虫的身份,若是奇数条足则输出 ?y7M#(火星文),偶数条足输出 Earth

如果输入数据存在多解,输出 Cannot Determine

样例 #1

样例输入 #1

3 5
011 1
110 1
101 0
111 1
010 1

样例输出 #1

4
Earth
?y7M#
Earth

样例 #2

样例输入 #2

5 7
01100 1
11000 1
10100 0
11100 1
00011 1
00000 0
11111 0

样例输出 #2

Cannot Determine

提示

评分标准

对于每一个测试点,如果你的输出文件与答案文件完全相同,该测试点得满分。

否则,对于存在唯一解的测试点,如果你正确回答所有千足虫的身份,将得到 \(50\%\) 的分数;

其他情况,该测试点得零分。

数据规模和约定

对于 \(20\%\) 的数据,满足 \(N=M\leq 20\)

对于 \(40\%\) 的数据,满足 \(N=M\leq 500\)

对于 \(70\%\) 的数据,满足 \(N\leq500\)\(M\leq 10^3\)

对于 \(100\%\) 的数据,满足 \(1\leq N\leq 10^3\)\(1\leq M\leq 2\times 10^3\)

题目分析

前置芝士:高斯消元

如果不会可以看看这篇文章:数学-高斯消元

首先观察到本题的方程式在模 \(2\) 意义下进行的,所以考虑令地球千足虫的足的总数为 \(0\),外星千足虫的足的总数为 \(1\)

因为若 \(x \equiv 1 \pmod 2\),则 \(x + 2n \equiv 1 \pmod 2\),其中 \(n\) 为正整数。与 \(0\) 同余也成立。

于是将问题转化为求解下列方程组

\[\begin{cases} \sum_{i=1}^{n} a_{1i} x_{i} \equiv b_1 \pmod 2\\ \sum_{i=1}^{n} a_{2i} x_{i} \equiv b_2 \pmod 2\\ \vdots\\ \sum_{i=1}^{n} a_{mi} x_{m} \equiv b_m \pmod 2\\ \end{cases} \]

在模 \(2\) 意义下,上面的操作跟异或运算是等价的,即 \((a \bmod 2 + b \bmod 2) \bmod 2 = a \operatorname{xor} b\),读者可以将 \(0,1\) 分别代入验证,这里不再赘述。

于是问题转化为求

\[\begin{cases} a_{11}x_1 \oplus a_{12}x_2 \oplus \dots\oplus a_{1n}x_n = b_1\\ a_{21}x_1 \oplus a_{22} x_2 \oplus \dots \oplus a_{2n}x_n= b_2\\ \vdots \\ a_{m1}x_1 \oplus a_{m2}x_2 \oplus \dots \oplus a_{mn}x_n = b_n \end{cases} \]

调用普通的高斯消元求解异或线性方程组即可求解出问题 \(2\) 的答案。

而对于问题 \(1\),由于题目保证数据不会出现冲突,那么如果我们用到第 \(k\) 个方程,那么前 \(k\) 个方程都不会存在冲突,于是在寻找主元时,第 \(1\) 个主元的行所在的最大行编号便是答案。

如果消元时存在主元为 \(0\) 的情况,即有多解,输出 \(\text{Cannot Determine}\) 即可。

代码实现

朴素解法

最普通的高斯消元,时间复杂度 \(O(n^2 m)\),最坏情况下无法通过此题。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bitset>

using namespace std;

const int maxm = 2e3 + 5;
const int maxn = 1e3 + 5;
int a[maxm][maxm];
int ans[maxn];
int n, m;
int tot = -1;

void gauss()
{
	//row  行 col 列
	int row = 1, col = 1;
	for (col = 1; col <= n; ++col)
	{
		int t = row;
		for (int i = row; i <= m; i++)
		{
			if (a[i][col])
			{
				t = i;
				break;
			}
		}
		if (!a[t][col])
		{
			puts("Cannot Determine");
			exit(0);
		}
		tot = max(tot, t);
		swap(a[row], a[t]);
		for (int i = row + 1; i <= m; i++)
		{
			if (!a[i][col]) continue;
			for (int j = n + 1; j >= col; j--)
			{
				a[i][j] = a[i][j] ^ a[row][j];
			}
		}
		++row;
	}
	cout << tot << endl;
	for (int i = n; i >= 1; i--)
	{
		ans[i] = a[i][n + 1];
		for (int j = i + 1; j <= n; j++)
		{
			if (a[i][j]) ans[i] ^= ans[j];
		}
	}
	for (int i = 1; i <= n; i++)
	{
		cout << (ans[i] ? "?y7M#" : "Earth") << endl;
	}
	return;
}

int main()
{
#ifndef ONLINE_JUDGE
#define LOCAL
	//freopen("in.txt","r",stdin);
#endif
	cin >> n >> m;
	for (int i = 1, x; i <= m; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			char y;
			cin >> y;
			a[i][j] = y - '0';
		}
		scanf("%d", &x);
		a[i][n + 1] = x;
	}
	gauss();
#ifdef LOCAL
	fprintf(stderr, "%f\n", 1.0 * clock() / CLOCKS_PER_SEC);
#endif
	return 0;
}

bitset 优化

由于是异或方程组,可以使用 bitset 优化,时间复杂度 \(O(\frac{n^2 m}{\omega})\) ,其中 \(\omega = 32\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bitset>

using namespace std;

const int maxm = 2e3 + 5;
const int maxn = 1e3 + 5;
bitset<maxm> a[maxm];
int ans[maxn];
int n, m;
int tot = -1;

void gauss()
{
	//row  行 col 列
	int row = 1, col = 1;
	for (col = 1; col <= n; ++col)
	{
		int t = row;
		for (int i = row; i <= m; i++)
		{
			if (a[i].test(col))
			{
				t = i;
				break;
			}
		}
		if (!a[t].test(col))
		{
			puts("Cannot Determine");
			exit(0);
		}
		tot = max(tot, t);
		swap(a[row], a[t]);
		for (int i = row + 1; i <= m; i++)
		{
			if (!a[i].test(col)) continue;
			a[i] ^= a[row];
		}
		++row;
	}
	cout << tot << endl;
	for (int i = n; i >= 1; i--)
	{
		ans[i] = a[i].test(n + 1);
		for (int j = i + 1; j <= n; j++)
		{
			if (a[i].test(j)) ans[i] ^= ans[j];
		}
	}
	for (int i = 1; i <= n; i++)
	{
		cout << (ans[i] ? "?y7M#" : "Earth") << endl;
	}
	return;
}

int main()
{
#ifndef ONLINE_JUDGE
#define LOCAL
	//freopen("in.txt","r",stdin);
#endif
	cin >> n >> m;
	char ch;
	for (int i = 1, x; i <= m; i++)
	{
		ch = getchar();
		for (int j = 1; j <= n; j++)
		{
			ch = getchar();
			a[i].set(j, ch - '0');
		}
		scanf("%d", &x);
		a[i].set(n + 1, x);
	}
	gauss();
#ifdef LOCAL
	fprintf(stderr, "%f\n", 1.0 * clock() / CLOCKS_PER_SEC);
#endif
	return 0;
}
posted @ 2025-01-13 14:31  vanueber  阅读(50)  评论(0)    收藏  举报