题目链接
https://ac.nowcoder.com/acm/contest/11166/I
题目大意
Alice和Bob在一个大小为 n ( 1 ≤ n ≤ 5000 ) n(1\leq n\leq 5000) n(1≤n≤5000) 的排列 P P P上玩游戏,双方轮流选择数字。
每一轮中,当前玩家需要选择一个比之前双方选择过的元素都大的元素。并且若该玩家之前选择了 P i P_i Pi,当前回合选择了 P j P_j Pj,则必须满足 j > i j>i j>i 。若有多种选择,则将等概率的随机选择一个合法的元素。
第一回合的玩家会随机选择一个元素,第二回合会随机选择一个比第一回合选择的数字大的元素。没有合法操作时结束游戏。
求游戏结束时,双方的总游戏回合数期望,答案对
998244353
998244353
998244353取模。
题解
这题需要运用期望问题和DP的算法,逆元可以通过预处理快速幂来计算。因为
1
≤
n
≤
5000
1 \leq n \leq 5000
1≤n≤5000 所以可以跑二重循环,判断是非符合条件进行计算。
由题意得DP转移式定义为第一维度表示我方上个选择的数,第二维度表示对方上个选择的数,即可得转移式为
当
p
x
<
p
y
p_x<p_y
px<py
d
p
[
x
]
[
y
]
=
1
+
1
c
∑
k
>
x
p
k
>
p
y
d
p
k
,
y
dp[x][y]=1+\tfrac{1}{c}\sum\limits_{k>x \ p_k>p_y}dp_{k,y}
dp[x][y]=1+c1k>x pk>py∑dpk,y
当
p
x
>
p
y
p_x>p_y
px>py
d
p
[
x
]
[
y
]
=
1
+
1
c
∑
k
>
y
p
k
>
p
x
d
p
x
,
k
dp[x][y]=1+\tfrac{1}{c}\sum\limits_{k>y \ p_k>p_x}dp_{x,k}
dp[x][y]=1+c1k>y pk>px∑dpx,k
(k为所选数,下方为条件)
此时可以统计当前的前缀方便计算。
s
u
m
y
=
∑
k
>
x
p
k
>
p
y
d
p
k
,
y
sum_y=\sum\limits_{k>x \ p_k>p_y}dp_{k,y}
sumy=k>x pk>py∑dpk,y
再采用
c
n
t
i
cnt_i
cnti表示选择
p
i
p_i
pi后可能选择的方案数
c
n
t
y
=
∑
k
>
x
p
k
>
p
y
1
cnt_y=\sum\limits_{k>x \ p_k>p_y}1
cnty=k>x pk>py∑1
快速幂的求解运用乘法逆元,即mod+2,故取模时要取模
m
o
d
+
2
=
998244353
mod+2=998244353
mod+2=998244353
最后将概率乘上次数即可。胡言乱语
参考代码
#include<bits/stdc++.h>
using namespace std;
const long long N=5e3+5;
const long long mod=998244353;
int n,f[N];
long long dp[N][N],pop[N],cnt[N],sum[N];
long long powmod(long long x,long long p) //快速幂预处理
{
long long ret=1;
while(p)
{
if(p&1)
ret=ret*x%mod;
x=x*x%mod;
p>>=1;
}
return ret;
}
void read(long long &x){
long long ret=0;
char c=getchar(),last;
while(c<'0'||c>'9')last=c,c=getchar();
while(c>='0'&&c<='9'){
ret=ret*10+c-'0';
c=getchar();
}
x=last=='-'?-ret:ret;
}
int main()
{
pop[0]=1;
scanf("%d",&n);
for(int i=1;i<=n;i++)
pop[i]=powmod(i,mod-2);
for(int i=1;i<=n;i++)
scanf("%d",&f[i]);
for(int i=n;i>=1;i--)
{
long long s=0,c=0;
for(int j=n;j>=0;j--)
{
if(i==j)
continue;
if(f[i]>f[j])
{
dp[i][j]=(s*pop[c]+1)%mod;
sum[j]+=dp[i][j];
sum[j]%=mod;
cnt[j]++;
}
else
{
dp[i][j]=(sum[j]*pop[cnt[j]]+1)%mod;
s+=dp[i][j];
s%=mod;
c++;
}
}
}
long long ans=0;
for(int i=1;i<=n;i++)
ans=(ans+dp[i][0])%mod;
ans=ans*pop[n]%mod; //计算
printf("%lld\n",ans);
}
总结
这道题大概是提高组C题的难度,将快速幂和DP+期望结合起来,值得一刷。有一定难度。