QOJ
题解
]
QOJ9042 Fast Bogosort 题解
不妨令 $f_n$ 表示一个长度为 $n$ 的随机排列的 shuffle 次数的期望,那么答案就是
\[\sum_i f(r_i-l_i+1)+[l_i<r_i]\]因为长度为 $1$ 的区间无需 shuffle。
令 $g_n$ 表示长度为 $n$ 的排列,满足其不能被划分为若干段的方案数,那么
\[H(x)=\sum_{i=0}^\infty i!x^i\\ n!=\sum_{i=1}^ng_i(n-i)!\\ H=1+H\ast G\\ G(x)=1-\frac1{H(x)}\]可以使用多项式求逆或者分治 NTT 来计算 $g_n$。
现在考虑求 $f_n$,可以写出递推式
\[f_n=\sum_{i=1}^n\frac{g_i(n-i)!}{n!}(f_i+[i>1]+f_{n-i})\]其中 $\frac{g_i(n-i)!}{n!}$ 表示第一段长度恰好为 $i$ 的概率。
我们这里只去计算第一段的贡献。注意,$f_n$ 表示的是一个随机排列的期望,所以我们只是相当于钦定了一个前缀,后面那部分的贡献的期望自然就是 $f_{n-i}$。
移项
\[f_n=\frac1{n!-g_n}\left(n!-(n-1)!+\sum_{i=1}^{n-1}g_if_i(n-i)!+g_if_{n-i}(n-i)!\right)\]分治 NTT 维护即可,可以在转移时记录一下多项式 $f_i\times g_i,f_i\times i!$ 方便转移。
constexpr int N=1e5+7;
int n,a[N];
poly F,G,H,FG,FH;
#define mid ((l+r)>>1)
void solve(int l,int r){
if(l==r){
F[l]+=H[l]-H[l-1];
F[l]/=H[l]-G[l];
FG[l]=F[l]*G[l];
FH[l]=F[l]*H[l];
return;
}
solve(l,mid);
poly tmp;
tmp.assign(FG.begin()+l,FG.begin()+mid+1);
poly t1=poly(H.begin(),H.begin()+r-l+1)*tmp;
tmp.assign(FH.begin()+l,FH.begin()+mid+1);
poly t2=poly(G.begin(),G.begin()+r-l+1)*tmp;
for(int i=mid+1;i<=r;i++)
F[i]+=t1[i-l]+t2[i-l];
solve(mid+1,r);
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(false);cin.tie(nullptr);
cin>>n;
FG.resize(n+1);
FH.resize(n+1);
H.assign(n+1,1);
for(int i=1;i<=n;i++)H[i]=H[i-1]*i;
G=-inV(H,n+1);G[0]+=1;
F.resize(n+1);
// for(int i=2;i<=n;i++){
// for(int j=1;j<=i-1;j++)
// F[i]+=G[j]*F[j]*H[i-j]+G[j]*F[i-j]*H[i-j];
// F[i]+=H[i]-H[i-1];
// F[i]/=H[i]-G[i];
// }
solve(1,n);
for(int i=1;i<=n;i++)cin>>a[i];
mint ans=0;
for(int l=1,r=1;l<=n;l=r+1){
int mn=a[l],mx=a[l];
for(r=l;;r++){
mn=min(mn,a[r]);
mx=max(mx,a[r]);
if(l==mn&&r==mx)break;
}
ans+=F[r-l+1];
if(l<r)ans+=1;
}
cout<<ans;
return 0;
}