QOJ.ac
QOJ
ID | 题目 | 提交者 | 结果 | 用时 | 内存 | 语言 | 文件大小 | 提交时间 | 测评时间 |
---|---|---|---|---|---|---|---|---|---|
#770628 | #7900. Gifts from Knowledge | ShallowMaple | WA | 8ms | 66340kb | C++14 | 4.1kb | 2024-11-21 22:47:08 | 2024-11-21 22:47:08 |
Judging History
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'