递推
玩了几天的组合排列之后,今天来玩玩递推吧。所谓递推,其实就是建立关系表达式,也就是建立两个式子之间的关系。上道题:
https://nanti.jisuanke.com/t/36677
解析
这道题,首先直接正解,发现贼麻烦,根本做不出来(我tm就没见过可以正解求出来的)。那么我们要尝试找关系了。要求期望,首先我们得知道分母是什么,分母是就是
1
−
n
1-n
1−n的可能存在的序列。分子就是这些序列长度的和。好的,知道这些后就该想想怎么计算呢? 首先对于分母,怎么求可能的序列。 我们尝试这样思考,最差的情况下,只要这个人翻开前
n
−
1
n-1
n−1张牌后,那么所有牌的位置他都知道了,也就是它再也不会犯错了。(犯错指翻了不该这次翻开的牌)。基于这样的情况,可以考虑递推,假设前
1
−
n
1-n
1−n已经放好,其序列数为
f
(
n
)
f(n)
f(n),那么考虑第
n
+
1
n+1
n+1张牌可能的位置。显然这张牌一定要放在最后吧,那么其他位置可放可不放。考虑不放,那么方案数就为
f
(
n
)
f(n)
f(n), 如果其他位置放呢?由前面的推论,如果前
n
−
1
n-1
n−1张牌都翻开后,那么他就不会犯错了,而这第
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)=n⋅f(n)+f(n)=(n+1)⋅f(n)很容易证明
f
(
n
)
=
n
!
f(n)=n!
f(n)=n!在知道方案数之后,就是求分子了,这些序列的长度之和。依然采用递推,假设
1
−
n
1-n
1−n的序列方案数为
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
1−n的每组原来个数分别
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)+2⋅f(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)+2⋅f(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;
}
新的开始,每天都要快乐哈!