AtCoder Beginner Contest 247 F - Cards // dp + 并查集
原题链接:F - Cards (atcoder.jp)
题意:
给定N张牌,每张牌正反面各有一个数,所有牌的正面、反面分别构成大小为N的排列P,Q。
思路:dp + 并查集
-
因为给定的是两个排列,所以每个点的度数为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#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; }