[ARC104] E - Random LIS

题面

题目描述

给定一个长度为 \(n\) 数组 \(A_i\),现对于 \(1\leq i\leq n\),等概率选取 \(B_i \in [1,A_i]\),求 \(B\) 的严格 LIS 期望长度,对 \(M=10^9+7\) 取模。

数据范围

  • \(1\leq n \leq 6\)

  • \(1\leq A_i \leq 10^9\)

题解

首先看到 \(n\) 很小,想到枚举每个元素排序后的排名 \(rk_i\),而后分别对每种情况计数和求 LIS。由于在本题中元素可以相等,所以级别是 \(O(n^n)\) 的而非 \(O(n!)\)

然后对于相同的 \(rk_i\) 肯定取的是最小的上界,即 \(\displaystyle lim_i=\min_{rk_j=i} a_j\)

\(\displaystyle m=\max_{i=1}^n rk_i\),则问题转化为:

求满足以下条件的长度为 \(m\) 的数组 \(c\) 的个数:

  • \(\forall 1\leq i\leq m\),有 \(c_i\in [1,lim_i]\)

  • \(\forall 1\leq i\lt m\),有 \(c_i\lt c_{i+1}\)

这是一个经典问题,首先将 \(lim\) 离散化,并将数轴按照 \(lim\) 分段,记构成第 \(i\) 段的集合为 \(v_i\)

\(f_{i,j}\) 为已经考虑了前 \(i\) 个元素,并且 \(c_i \in v_j\) 内的方案数。

考虑枚举分段点 \(k\) 使得 \(\forall k\leq p\leq i\),都有 \(c_p\in v_j\),且 \(c_{k-1}\notin v_j\)。同时枚举 \(c_{k-1}\) 在哪一段内。

有转移式: \(\displaystyle f_{i,j}=\sum_{k\leq i} \binom{|v_j|}{i-k+1} \sum_{p\lt j} f_{k-1,p}\)

二项式系数可以暴力计算,时间复杂度 \(O(n^{n+4}\log M)\)

#include<bits/stdc++.h>

using namespace std;

#define endl '\n'
const int N=6+9;
const int mod=1e9+7;
const int inf=1e9+7;
inline void AddAs(int &x,int y){if((x+=y)>=mod) x-=mod;}
inline void SubAs(int &x,int y){if((x-=y)<0) x+=mod;}
inline void MulAs(int &x,int y){x=1ll*x*y%mod;}
inline int Add(int x,int y){if((x+=y)>=mod) x-=mod;return x;}
inline int Sub(int x,int y){if((x-=y)<0) x+=mod;return x;}
inline int Mul(int x,int y){return 1ll*x*y%mod;}
inline int QPow(int x,int y){
    int res=1;
    while(y){
        if(y&1) MulAs(res,x);
        MulAs(x,x);
        y>>=1;
    }
    return res;
}
#define Inv(x) QPow(x,mod-2)

int rk[N],a[N],n;
inline bool Check(){
    vector<int> vis(n+1,0);
    vis[0]=1;
    for(int i=1;i<=n;i++) vis[rk[i]]=1;
    for(int i=1;i<=n;i++) if(vis[i]&&!vis[i-1]) return 0;
    return 1;
}
int lim[N],f[N][N],m,ans;
inline int C(int n,int m){
    if(m<0||n<m) return 0;
    if(!m) return 1;
    else return Mul(C(n-1,m-1),Mul(n,Inv(m)));
}
inline int LIS(){
    vector<int> f(n+1);
    f[0]=0;
    int res=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<i;j++) if(rk[j]<rk[i]) f[i]=max(f[i],f[j]+1);
        res=max(res,f[i]);
    }
    return res;
}
inline void Work(){
    if(!Check()) return ;
    m=0;
    for(int i=1;i<=n;i++) m=max(m,rk[i]);
    for(int i=1;i<=m;i++) lim[i]=inf;
    for(int i=1;i<=n;i++) lim[rk[i]]=min(lim[rk[i]],a[i]);
    
    vector<int> val({0});
    for(int i=1;i<=m;i++) val.push_back(lim[i]);
    sort(val.begin(),val.end());
    val.erase(unique(val.begin(),val.end()),val.end());
    int tot=val.size()-1;
    for(int i=1;i<=m;i++) lim[i]=lower_bound(val.begin(),val.end(),lim[i])-val.begin();
    
    memset(f,0,sizeof f);
    f[0][0]=1;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=tot;j++){
            for(int k=1;k<=i;k++){
                int tmp=C(val[j]-val[j-1],i-k+1);
                bool flag=0;
                for(int p=k;p<=i;p++) if(lim[p]<j) flag=1;
                if(flag) continue ;
                for(int p=0;p<j;p++) AddAs(f[i][j],Mul(f[k-1][p],tmp));
            }
        }
    }
    int tmp=0;
    for(int i=1;i<=lim[m];i++) AddAs(tmp,f[m][i]);
    AddAs(ans,Mul(tmp,LIS()));
}
inline void Solve(int i){
    if(i>n) return Work();
    for(int j=1;j<=n;j++){
        rk[i]=j;
        Solve(i+1);
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);

    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];

    Solve(1);
    int mul=1;
    for(int i=1;i<=n;i++) MulAs(mul,a[i]);
    cout<<Mul(ans,Inv(mul))<<endl;

    return 0;
}
posted @ 2025-04-09 09:48  JoeyJiang  阅读(16)  评论(0)    收藏  举报