白玉楼前
白玉楼前
【题目背景】
“一觉醒来怎么半灵又不见了?一定是幽幽子吃了。”
“幽幽子你给我吐出来!”
“我这边有个游戏玩不过去,你帮我玩过去我就吐出来。”
【题目描述】
妖梦现在要玩幽幽子的游戏,她才能拿回自己的半灵。
游戏规则是这样的:
幽幽子有n nn 个点,现在她让妖梦对每个点随机一条出边(随机到每个点的概率都相等),
然后得到一张图。(注意:可以自环)
如果这张图任意一个点沿着边走两步(显然这样的走法唯一)都能到达自身,则幽幽子可以通关。
现在幽幽子想问妖梦,她通关的概率是多少?
两个图不同,当且仅当存在一条边出现在图A 中且不出现在图B 中。图中的点有编号,边无编号。
答案mod 998244353
【数据范围】
T≤5×105,n≤5×105 T \leq 5\times 10^5,n\leq 5\times 10^5T≤5×10
solution
本题求1~n的排列的对偶排列个数,即满足p[p[i]]=i的p[i]个数.
令f[i]表示前i个数的答案
f[i]=f[i-1]+(i-1)*f[i-2];
也就是它可以和自己对偶p[i]=i;
或者和任意一个人(i-1) 乘上 i-2个人的答案
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 500005
#define ll long long
#define mod 998244353
using namespace std;
int n;
ll f[maxn],ny[maxn];
ll work(ll a,int num){
ll p=a,x=1;
while(num){
if(num&1)x=x*p;
p=p*p;p%=mod;x%=mod;num>>=1;
}
return x;
}
int main()
{
freopen("youmu.in","r",stdin);
freopen("youmu.out","w",stdout);
n=500000;
f[0]=1;f[1]=1;
for(int i=2;i<=n;i++){
f[i]=f[i-1]+(ll)(i-1)*f[i-2]%mod;f[i]%=mod;
}
for(int i=0;i<=n;i++){
ny[i]=work(work(i,i),mod-2);
}
int T;
cin>>T;
for(int i=1;i<=T;i++){
scanf("%d",&n);
ll ans=ny[n]*f[n];ans=(ans%mod+mod)%mod;
printf("%lld\n",ans);
}
return 0;
}