白玉楼前

白玉楼前
【题目背景】
“一觉醒来怎么半灵又不见了?一定是幽幽子吃了。”
“幽幽子你给我吐出来!”
“我这边有个游戏玩不过去,你帮我玩过去我就吐出来。”
【题目描述】
妖梦现在要玩幽幽子的游戏,她才能拿回自己的半灵。
游戏规则是这样的:
幽幽子有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;
}

 

posted @ 2018-11-03 10:42  liankewei123456  阅读(115)  评论(0编辑  收藏  举报