Cards(ABC 247 F)

AtCoder - abc247_f

题目大意

你有N张卡片,每张卡有正反两面,正面有一个数字Pi,反面有一个数字Qi,数列PQ都是(1,2,,N)的全排列,问有多少种选择方法使得N个数都至少一次出现在被选中的牌上,答案对998244353取模。(1N2×105)

思路

首先,我们建一张图,把每张牌正反的两个数连起来,而由于PQ是全排列,每个数都只会出现两次,每个点的度都是2,所以我们建出来的图会由很多个环构成。然后我们先来考虑一个简单一点的问题:1,2,,mm个数中相邻的两个数至少得选一个的方案数是多少。就是分类讨论m是否选,这样的话,设答案为f(m),则满足f(1)=2,f(2)=3,,f(m)=f(m1)+f(m2)。然后现在我们要求的是1,2,,mm个数围成一个圈时连着一个点的两条边至少得选一条的方案数,设答案为g(m),分类讨论一下1m是否相连,就能得到g(1)=1,g(2)=3,,g(m)=f(m1)+f(m3),观察一下可以发现g(m)=LmL为卢卡斯数列。这样,我们只要把每个环的答案乘起来就结束了。

代码

#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;
}

__EOF__

本文作者Jerry-Black
本文链接https://www.cnblogs.com/Jerry-Black/p/16208282.html
关于博主:小蒟蒻一只( ̄^ ̄)ゞ
版权声明:转载请注明来源哟~ QAQ
声援博主:UP UP UP !!!
posted @   Jerry_Black  阅读(324)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示