题目链接:Luogu P6059
我傻了,比赛时写错了一个小细节,一看(nle 50)还以为想假了
先把最大的(a_i)特判掉,答案显然为(n-1)。
我们枚举每一个(a_i)分别计算答案,找到左右距(a_i)最近且大于(a_i)的(a_l,a_r),那么(a_i)被击倒只有([l,i])或者([i,r])间的容器全部进行决斗。
(若(a_l)和(a_r)在此之前和其他容器决斗只可能不变或变得更大)
枚举(a_i)在第(j)轮被淘汰,设概率为(p_{i,j}),则(a_i)的期望存活轮数为(sum_{1le j<n}limits p_{i,j}*(j-1))
考虑怎么求(p_{i,j}),设(g_{i,j})表示(a_i)在前(j)轮的某一次被淘汰的概率,则(p_{i,j}=g_{i,j}-g_{i,j-1})
因为一共进行了(n-1)次决斗,设(Calc(i,j))表示在这(n-1)轮决斗中选出(i)轮决斗全部在前(j)轮的概率
那么对于(g_{i,j}),要么被左边淘汰,要么被右边淘汰,容斥一下则
(g_{i,j}=Calc(i-l,j)+Calc(r-i,j)-Calc(r-l,j))
(被左边淘汰的概率+被右边淘汰的概率-去掉两边决斗全部在前(j)场的重复计算)
当然如果某一边没有比(a_i)大的容器那答案就是(Calc(i-l,j))或者(Calc(r-i,j))
考虑计算(Calc(i,j))。(i)场在前(j)场的方案数:(A_j^i=frac{j!}{(j-i)!}),其他决斗的方案数:((n-1-i)!),总方案数:((n-1)!)
则(Calc(i,j)=frac{j!(n-1-i)!}{(j-i)!(n-1)!})
预处理阶乘及逆元,暴力计算即可做到(O(n^2)),不知道能不能继续优化,反正O(n^2)足够了
时间复杂度 (O(n^2))
空间复杂度 (O(n))
代码:
#include <cstdio>
typedef long long ll;
const int N=55,P=998244353;
int n,a[N],Fac[N],Inv[N],Ans[N];
ll Pow(ll a,int b,ll s=1){for(;b;b>>=1,a=a*a%P)if(b&1)s=s*a%P;return s;}
inline int Calc(int x,int l)//x场全部在前l场的概率
{
if(x>l)return 0;
return (ll)Fac[l]*Inv[l-x]%P*Fac[n-1-x]%P*Inv[n-1]%P;
}
int main()
{
scanf("%d",&n);
for(int i=Fac[0]=1;i<=n;++i)Fac[i]=(ll)Fac[i-1]*i%P;
Inv[n]=Pow(Fac[n],P-2);
for(int i=n;i>=1;--i)Inv[i-1]=(ll)Inv[i]*i%P;
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
for(int i=1;i<=n;++i)
{
int l=i-1,r=i+1;
while(l>=1&&a[l]<a[i])--l;
while(r<=n&&a[r]<a[i])++r;
if(l<1&&r>n){Ans[i]=n-1;continue;}
int Res=0,Pp=0;
for(int j=1;j<n;++j)
{
int Np=0;
if(l>=1)Np=Calc(i-l,j);
if(r<=n)Np=(Np+Calc(r-i,j))%P;
if(l>=1&&r<=n)Np=(Np-Calc(r-l,j)+P)%P;//容斥
Res=(Res+ll(j-1)*(Np-Pp+P))%P,Pp=Np;//这里没有p和g数组,Np代表g_{i,j},Pp代表g_{i,j-1}
}
Ans[i]=Res;
}
for(int i=1;i<=n;++i)printf("%d%c",Ans[i],i==n?'
':' ');
return 0;
}