381 字words
2 分钟minutes
Cards (ABC 247 F)
题目大意
你有张卡片,每张卡有正反两面,正面有一个数字,反面有一个数字,数列和都是的全排列,问有多少种选择方法使得个数都至少一次出现在被选中的牌上,答案对取模。
思路
首先,我们建一张图,把每张牌正反的两个数连起来,而由于和是全排列,每个数都只会出现两次,每个点的度都是,所以我们建出来的图会由很多个环构成。然后我们先来考虑一个简单一点的问题:这个数中相邻的两个数至少得选一个的方案数是多少。就是分类讨论是否选,这样的话,设答案为,则满足。然后现在我们要求的是这个数围成一个圈时连着一个点的两条边至少得选一条的方案数,设答案为,分类讨论一下和是否相连,就能得到,观察一下可以发现,为卢卡斯数列。这样,我们只要把每个环的答案乘起来就结束了。
代码
#include<bits/stdc++.h>using namespace std;long long a[200005];long long mod=998244353;int p[200005];int q[200005];int fa[200005];int siz[200005];int vis[200005];int findd(int x){ return fa[x]==x?x:(fa[x]=findd(fa[x]));}int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&p[i]); for(int i=1;i<=n;i++)scanf("%d",&q[i]); for(int i=1;i<=n;i++){fa[i]=i;siz[i]=1;} for(int i=1;i<=n;i++)if(findd(p[i])!=findd(q[i])) { siz[findd(q[i])]+=siz[findd(p[i])]; fa[findd(p[i])]=findd(q[i]); } a[0]=2; a[1]=1; for(int i=2;i<=n;i++)a[i]=(a[i-1]+a[i-2])%mod; long long ans=1; for(int i=1;i<=n;i++)if(!vis[findd(i)]) { ans=ans*a[siz[findd(i)]]%mod; vis[findd(i)]=1; } printf("%lld\n",ans); return 0;} Cards (ABC 247 F)
https://jerryblack.vercel.app/posts/cards-abc247f/