AtCoder Beginner Contest 247 F - Cards // dp + 并查集

原题链接:https://atcoder.jp/contests/abc247/tasks/abc247_f

 

题意:

给定N张牌,每张牌正反面各有一个数,所有牌的正面、反面分别构成大小为N的排列P,Q。

求有多少种摆放方式,使得N张牌朝上的数字构成一个1~N的排列。

 

思路:dp + 并查集

  • 建图:有1~N的顶点,然后Pi跟Qi连一条边。

    因为给定的是两个排列,所以每个点的度数为2,因而建出的图必然是由几个独立的环构成。

    根据乘法原理,答案就等于每个环的方案数相乘。

  • 求每个环的方案数:

    假设环的大小为n(点的数量),dp[n]表示这样的环的方案数:dp[1] = 1,dp[2] = 3,当 n >= 3 时,dp[n] = dp[n - 1] + dp[n - 2]。

    上述结论证明:因为每个点都要选出,每个点又跟两条边相连,因此选边时:若此边不选,下条边必须选;若此边已选,下条边可选可不选。那么对于n - 1个点的环,插入第n个点,就由两种情况转移过来。

  • 判环及求环的大小:

    并查集可以很好的完成任务 : )

    (不得不说并查集真的是简洁又好用的数据结构

 

代码参考:

//Jakon:dp + 并查集
#include <bits/stdc++.h>
#define int long long
using namespace std;

const int mod = 998244353;
const int N = 200010;

int n, p[N], q[N], dp[N], fa[N], siz[N];

int find(int x)
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

signed main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> p[i];
    for(int i = 1; i <= n; i++) cin >> q[i];

    dp[1] = 1, dp[2] = 3;
    for(int i = 3; i <= n; i++) dp[i] = (dp[i - 1] + dp[i - 2]) % mod;

    for(int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1;
    for(int i = 1; i <= n; i++) {
        int x = find(p[i]), y = find(q[i]);
        if(x != y) fa[x] = y, siz[y] += siz[x];
    }
    
    int ans = 1;
    for(int i = 1; i <= n; i++) 
        if(fa[i] == i) ans = ans * dp[siz[i]] % mod;
    cout << ans << endl;

    return 0;
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值