递推

递推

玩了几天的组合排列之后,今天来玩玩递推吧。所谓递推,其实就是建立关系表达式,也就是建立两个式子之间的关系。上道题:
https://nanti.jisuanke.com/t/36677

解析

这道题,首先直接正解,发现贼麻烦,根本做不出来(我tm就没见过可以正解求出来的)。那么我们要尝试找关系了。要求期望,首先我们得知道分母是什么,分母是就是 1 − n 1-n 1n的可能存在的序列。分子就是这些序列长度的和。好的,知道这些后就该想想怎么计算呢? 首先对于分母,怎么求可能的序列。 我们尝试这样思考,最差的情况下,只要这个人翻开前 n − 1 n-1 n1张牌后,那么所有牌的位置他都知道了,也就是它再也不会犯错了。(犯错指翻了不该这次翻开的牌)。基于这样的情况,可以考虑递推,假设前 1 − n 1-n 1n已经放好,其序列数为 f ( n ) f(n) f(n),那么考虑第 n + 1 n+1 n+1张牌可能的位置。显然这张牌一定要放在最后吧,那么其他位置可放可不放。考虑不放,那么方案数就为 f ( n ) f(n) f(n), 如果其他位置放呢?由前面的推论,如果前 n − 1 n-1 n1张牌都翻开后,那么他就不会犯错了,而这第 n + 1 n+1 n+1张牌只要不是最后翻开,那么就是他犯错了,所有这张牌只能在前n个位置上插入。所以可以有关序列数的递推式为:
f ( n + 1 ) = n ⋅ f ( n ) + f ( n ) = ( n + 1 ) ⋅ f ( n ) f(n+1)=n \cdot f(n)+f(n)=(n+1)\cdot f(n) f(n+1)=nf(n)+f(n)=(n+1)f(n)很容易证明 f ( n ) = n ! f(n)=n! f(n)=n!在知道方案数之后,就是求分子了,这些序列的长度之和。依然采用递推,假设 1 − n 1-n 1n的序列方案数为 s u m ( n ) sum(n) sum(n),那么考虑第 n + 1 n+1 n+1个数加入对总和产生的影响。首先是只放在最后的影响,答案很显然为 s u m ( n ) + f ( n ) sum(n)+f(n) sum(n)+f(n), 那么加在前面呢?假设 1 − n 1-n 1n的每组原来个数分别 a 1 , a 2 , ⋯ a f ( n ) a_1, a_2,\cdots a_{f(n)} a1,a2,af(n), 加入两个数对每个的影响为 ( a i + 2 ) ⋅ n (a_i+2)\cdot n (ai+2)n ,将所有的累加为 n ⋅ ( s u m ( n ) + 2 ⋅ f ( n ) n\cdot (sum(n)+2\cdot f(n) n(sum(n)+2f(n), 那么答案就为 n ⋅ ( s u m ( n ) + 2 ⋅ f ( n ) ) + s u m ( n ) + f ( n ) n\cdot(sum(n)+2\cdot f(n))+sum(n)+f(n) n(sum(n)+2f(n))+sum(n)+f(n)

AC代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f
typedef long long ll;
const int maxn=1e7+5;
const int maxm=1e3+5;
const ll mod=998244353;
using namespace std;
ll a[maxn],sum[maxn];

ll poww(ll a,ll b){
    ll ans=1,base=a;
    while(b){
        if(b&1){
            ans*=base;
            ans%=mod;
        }
        b>>=1;
        base*=base;
        base%=mod;
    }
    return ans;
}

void init(){
    a[0]=1,a[1]=1;
    sum[0]=0,sum[1]=1;
    for(int i=2;i<=1e7;i++){
        a[i]=a[i-1]*i;
        a[i]%=mod;
        sum[i]=(i-1)*(sum[i-1]+2*a[i-1])%mod+(sum[i-1]+a[i-1])%mod;
        sum[i]%=mod;
    }
}

int main(){
    init();
    int n;
    scanf("%d",&n);
    if(n==0){
        cout<<"0"<<endl;
        return 0;
    }
    ll num=poww(a[n],mod-2);
    ll ans=(sum[n]*num)%mod;
    printf("%lld\n",ans);
    return 0;
}

新的开始,每天都要快乐哈!
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值