Codeforces1534C

问题描述

  • 给你两列数,你可以每次将同一列的数上下交换,要你求出有多少种方案能使每一列每一行不出现重复的数字。

思路分析

  • 如果把某一列两个数交换了的话,那么这两行必定有数出现两次,所以就要想办法把重复出现的数交换回去,这样的话,最后肯定会有一个正确方案。
  • 我们如何来算这个东西呢?经过上面的解释我们可以想到,如果有 i i i对数能够通过交换使得有一个正确方案的话,我们只需要找到共有多少这样的对即可。
  • 这样的对满足什么条件呢?其实我们把第一行该数连上与他同列的数,再将与他同列的数连上第一行这个数所在位置,然后再依此类推,我们总能找到一个结束点,实际上该题就是要我们求有几种这样的圈。
  • 怎么求这样的圈呢?我用的是并查集找环,也就是把第二行连上第一行。然后判断即可。
  • 然后就是求答案了,其实对于每个环,我们都有选或者不选的情况,所以答案就是 2 环 数 2^{环数} 2

代码如下

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 4e5 + 10;
int fa[maxn];
int a[maxn];
int b[maxn];
int find(int x)
{
        return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}
ll qpow(ll a, ll b, ll p)
{
        ll ans = 1;
        while (b)
        {
                if (b & 1)
                {
                        ans = ans * a % p;
                }
                a = a * a % p;
                b >>= 1;
        }
        return ans;
}
int main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        int t;
        cin >> t;
        while (t--)
        {
                memset(a, 0, sizeof(a));
                memset(b, 0, sizeof(b));
                int n;
                cin >> n;
                for (int i = 1; i <= n; i++)
                {
                        cin >> a[i];
                        fa[i] = i;
                }
                int cnt = 0;
                for (int i = 1; i <= n; i++)
                {
                        cin >> b[i];
                        if (find(a[i]) == b[i])
                        {
                                cnt++;
                        }
                        else
                                fa[find(b[i])] = find(a[i]);
                }
                cout << qpow(2, cnt, 1000000007) << endl;
        }
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值