QOJ.ac

QOJ

ID题目提交者结果用时内存语言文件大小提交时间测评时间
#770628#7900. Gifts from KnowledgeShallowMapleWA 8ms66340kbC++144.1kb2024-11-21 22:47:082024-11-21 22:47:08

Judging History

你现在查看的是最新测评结果

  • [2024-11-21 22:47:08]
  • 评测
  • 测评结果:WA
  • 用时:8ms
  • 内存:66340kb
  • [2024-11-21 22:47:08]
  • 提交

answer

/*
题意:给定一个01矩阵,可以选择将每一行是否进行左右翻转,求让每一列最多有一个1的方案数
思路:首先是答案肯定是0的情况:如果对于第 i 列和第 n - i + 1 列的 1 的数量之和大于2,那么肯定是无法做到每一列至多一个 1 ,方案数就是0
然后看如何翻转:
1:如果第 i 列和第 n - i + 1 列的 1 的数量之和小于等于 1 那么这 个 1 所在的一行翻转和不翻转都可以
2:如果第 i 列和第 n - i + 1 列的 1 的数量之和为2
  (1):如果这两个 1 在同一列,那么这两行(指的是这两个1所在的行)必须翻转一个
  (2):如果这两个 1 不在同一列,那么这两行(同上)必须要不都翻转,要不都不翻转
接着如何实现:
我们使用一个互斥并查集:我们定义 i 为翻转这一行,i + n 为不翻转这一行
我们定义 i1 ,i2 分别是 2 中两个 1 所在的行
对于 2.(1):我们将 (i1, i2 + n)以及 (i1 + n, i2)这两对点合并
对于 2.(2):我们将 (i1, i2)以及 (i1 + n, i2 + n)这两对点合并
最后如果存在(i, i + n)在一个联通块,那就是互斥了,方案数是0
当所有约束条件都加进并查集后,最终的连通块数量k决定了可以选择的翻转方案数。
每个连通块可以选择翻转或不翻转),那么总的方案数就是每个连通块有 2 种选择,最终方案数为 pow(2, k)。
然而最终的答案是 pow(2, k / 2),而不是 pow(2, k),是因为每两个互斥的行(组成一个连通块)会共享一个翻转状态。
每个连通块之间有 2 种可能的选择(要么都翻转,要么都不翻转),而不仅仅是每行独立地有 2 种选择
*/

#include<bits/stdc++.h>
#define endl '\n'
#define int long long

using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;

const int N = 2e6 + 5, mod = 1e9 + 7;
int n, m, k, x, y, T;
int fa[N];
string str[N];

int getfa(int x){
	if(fa[x] != x) fa[x] = getfa(fa[x]);
	return fa[x];
}

void merg(int a, int b){
	int af = getfa(a), bf = getfa(b);
	if(af != bf) fa[af] = bf;
}


ll fastpow(ll a, ll n){
	ll base = a, res = 1;
	while(n){
		if(n & 1) res = (res * base) % mod;
		base = (base * base) % mod;
		n >>= 1;
	}
	return res;
} 

void solve()
{
	cin >> n >> m;
	for(int i = 1; i <= 2 * n; i ++){
		fa[i] = i;
	}
	
	for(int i = 1; i <= n; i ++){
		cin >> str[i];
		str[i] = " " + str[i];
	}
	
	vector<int> cnt(m + 10, 0);
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= m; j ++){
			if(str[i][j] == '1') cnt[j] ++;
		}
	
	if(m % 2 == 1){
		if(cnt[(m + 1) / 2] > 2){
			cout << 0 << endl;
			return;
		}
	}
	for(int i = 1; i <= m; i ++){
		if(cnt[i] + cnt[m + 1 - i] > 2){
			cout << 0 << endl;
			return;
		}
	}
	
	for(int i = 1, j = m; i <= j; i ++, j --)
	{
		vector<int> v1, v2;
		for(int r = 1; r <= n; r ++){
			if(str[r][i] == '1') v1.push_back(r); //vector记录每对 列和其镜像列 的1的行号
			if(str[r][j] == '1') v2.push_back(r); //且保证每个vector最多只有两个1
		}
		if(v1.size() == 2) merg(v1[0], v1[1] + n), merg(v1[0] + n, v1[1]); //如果这两个 1 在同一列,那么这两行必须翻转一个
		if(v2.size() == 2) merg(v2[0], v2[1] + n), merg(v2[0] + n, v2[1]); //如果这两个 1 在同一列,那么这两行必须翻转一个
		//如果这两个 1 不在同一列,那么这两行必须要不都翻转,要不都不翻转
		for(int ii = 0; ii < v1.size(); ii ++)
			for(int jj = 0; jj < v2.size(); jj ++)
				merg(v1[ii], v2[jj]), merg(v1[ii] + n, v2[jj] + n);
	}
	
	for(int i = 1; i <= n; i ++){
		if(getfa(i) == getfa(i + n)){
			cout << 0 << endl;
			return;
		}
	}
	
	//统计连通块数量
	int cntt = 0;
	for(int i = 1; i <= 2 * n; i ++){
		if(i == getfa(i)) cntt ++;
	}
	int res = 1ll;
	for(int i = 1; i <= cntt; i ++){
		res = res * 2 % mod;
	}
	cout << res % mod << endl;
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0);
	
	cin >> T;
	while(T--) solve();
	
	return 0;
}


详细

Test #1:

score: 0
Wrong Answer
time: 8ms
memory: 66340kb

input:

3
3 5
01100
10001
00010
2 1
1
1
2 3
001
001

output:

16
0
4

result:

wrong answer 1st numbers differ - expected: '4', found: '16'