2023年ICPC济南站G题

文章讨论了一个关于给定01矩阵的翻转问题,目标是使每列最多有一个1。通过分析,确定了当列中1的总数为2时的情况,并利用扩展域并查集来计算满足条件的连通块数量。代码展示了如何通过并查集操作来解决此问题。
摘要由CSDN通过智能技术生成

Gifts from Knowledge

题意

给定r行c列的01矩阵,可以对每一行进行翻转,也可以不进行操作,翻转的意思是将一开始的序列{ a 1 , a 2 , … , a n a_{1}, a_{2}, \dots, a_{n} a1,a2,,an}翻转为{ a n , … , a 2 , a 1 a_{n},\dots,a_{2},a_{1} an,,a2,a1},使得最终得到的01矩阵的每一列的1的个数不多于1个,问有多少种翻转方案,对结果模1e9+7

分析

容易知道的是,设第i列和第c-i+1列总的1的个数为x,当x ≥ \geq 3后,无解,当x ≤ \leq 1时,无论怎么操作,这两列永远合法,所以最终只需要考虑当x=2时。

当x=2时有两种情况,假设两个1分别位于第x和y行

  • 初始时,两个1位于同一列,那么能进行的操作就是{(翻转x,y不动),(不动x,翻转y)}
  • 初始时,两个1位于不同列,那么能进行的操作就是{(翻转x,翻转y),(不动x,不动y)}

由此两种情况我们可以发现,当同一对x和y同时拥有以上两种情况的时候是无解的,比如:

2 6
101010
100100

这个样例就是无解的,因此需要做的就是使用扩展域并查集,i表示翻转第i行,i+n表示不翻转第i行,因此,当两个1位于同一列的时候,合并(x, y+n)和(x+n, y),当两个1位于不同列的时候,合并(x, y)和(x+n, y+n),注意要判断i和i+n是否在一个连通块内,答案就是 2 连通块的个数 2^{连通块的个数} 2连通块的个数

代码

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int mod = 1e9 + 7;
void Solve() {
    int n, m;
    cin >> n >> m;
    vector<string> a(n + 1);
    vector<vector<int> > G(m + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        a[i] = ' ' + a[i];
        bool ok = false;
        for (int j = 1; j <= m; j++) {
            if (a[i][j] == '1') {
                G[j].emplace_back(i);
                ok = true;
            }
        }
    }
    vector<int> fa(2 * n + 1);
    iota(fa.begin(), fa.end(), 0);
    auto getfa = [&](int x, auto getfa) -> int {
        return x == fa[x] ? x : fa[x] = getfa(fa[x], getfa);
    };
    for (int i = 1; i <= m; i++) {
        if (i == m - i + 1) {
            if (G[i].size() > 1) {
                cout << "0\n";
                return;
            }
        } else {
            if (G[i].size() + G[m - i + 1].size() >= 3) {
                cout << "0\n";
                return;
            }
            if (G[i].size() + G[m - i + 1].size() == 2) {
                vector<int> z;
                for (auto it : G[i]) {
                    z.emplace_back(it);
                }
                for (auto it : G[m - i + 1]) {
                    z.emplace_back(it);
                }
                if (G[i].size() == 1) {
                    int f1 = getfa(z[0], getfa), f2 = getfa(z[1], getfa);
                    if (f1 != f2) {
                        fa[f1] = f2;
                    }
                    f1 = getfa(z[0] + n, getfa), f2 = getfa(z[1] + n, getfa);
                    if (f1 != f2) {
                        fa[f1] = f2;
                    }
                } else {
                    int f1 = getfa(z[0], getfa), f2 = getfa(z[1] + n, getfa);
                    if (f1 != f2) {
                        fa[f1] = f2;
                    }
                    f1 = getfa(z[0] + n, getfa), f2 = getfa(z[1], getfa);
                    if (f1 != f2) {
                        fa[f1] = f2;
                    }
                }
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        if (getfa(i, getfa) == getfa(i + n, getfa)) {
            cout << "0\n";
            return;
        }
    }
    LL ans = 1;
    for (int i = 1; i <= n; i++) {
        if (getfa(i, getfa) == i) {
            ans = ans * 2 % mod;
        }
    }
    cout << ans << '\n';
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--) {
        Solve();
    }
    return 0;
}

吐槽

济南站现场一直没想到合适的反例,一直wa到结束,不然就有牌牌了QAQ

  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值