【Good Bye 2020 F】Euclid's nightmare

题目链接

链接

翻译

给你 \(n\) 个向量, 每个向量都是 \(m\) 维的,在他们之间你可以做任意次数的模 \(2\) 加法,用 \(T\) 表示你能用这些向量得到的向量, 然后

问你用 \(n\) 个向量中最少多少个向量就已经足够表示出 \(T\) 了,设这个最少用的向量集合为 \(S\)

让你求出\(T\) 的大小, 以及 \(S\) 的大小, 和 \(S\) 的各个向量下标,多个答案,要求 \(S\) 的字典序也最小。

给的 \(n\) 个向量中,只会有两个或者一个 \(1\),其余都是 \(0\)

题解

把每个维度都看出一个顶点,那么所给的 \(n\) 个向量中,为 \(1\) 的两个维度就连一条边。

显然就可以形成一个图了。

如果某个向量中只有一个 \(1\),那么就增加一个维度,让那个向量变成 \(m+1\) 维的,然后第 \(m+1\) 维是 \(1\)

这样做有啥效果呢?

会发现图中你任意走一条路径的话(路上的节点所在维上的 \(1\) 进行累加),最终的结果就是让路径头尾两个节点 \(x\)\(y\) (中间节点因为偶数次累加都是 \(0\))

对应的维度上的数字变成 \(1\),那么此时如果有一条边 \(x,y\) 直接连接 \(x\)\(y\), 也就对应了有一个向量在第 \(x\) 和第 \(y\)

维上都是 \(1\),那么形成了一个环,可以发现这条边 \((x,y)\) 对应的向量是不需要的,冗余的。因为直接沿着路径累加就能替代

这个向量了。

所以思路就是,让形成的图没有环, 可能有多个联通分量,所以最后所求的是一个森林。再进一步,字典序要求最小。

把边上标上向量的下标作为权重。

那么,最后要求的 \(S\) 就是一个 \(MST\) 了。

最后,能表示的向量个数 |T| 这样求:设这个 \(MST\) 的边的个数为 \(cnt\), 答案就为 \(2^{cnt}\), 每个向量用或者不用,因为是最小的表示

了,所以 \(S\) 中的向量只要增减,都能对应一个独一无二的向量。

代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 5e5;
const int MOD = 1e9 + 7;

int n, m;
int f[N + 10];

int ff(int x) {
	if (f[x] == x) {
		return x;
	}
	else {
		return f[x] = ff(f[x]);
	}
}

vector<int> ans;

int main() {
	#ifdef LOCAL_DEFINE
		freopen("in.txt", "r", stdin);
	#endif
	ios::sync_with_stdio(0), cin.tie(0);
	int T;
	T = 1;
	while (T--) {
		cin >> n >> m;
		for (int i = 1; i <= m + 1; i++) {
			f[i] = i;
		}
		for (int i = 1; i <= n; i++) {
			int k;
			cin >> k;
			int x, y = m + 1;
			cin >> x;
			if (k == 2) {
				cin >> y;
			}
			int r1 = ff(x), r2 = ff(y);
			if (r1 != r2) {
				f[r1] = r2;
				ans.push_back(i);
			}
		}
		int cnt = 1;
		for (int i = 0; i < (int)ans.size(); i++) {
			cnt = (cnt + cnt) % MOD;
		}
		cout << cnt << " " << (int)ans.size() << endl;
		for (int x : ans) {
			cout << x << " ";
		}
		cout << endl;
	}
	return 0;
}
posted @ 2021-02-15 15:42  AWCXV  阅读(71)  评论(0编辑  收藏  举报