- 给定一个长度为(n)的数组(a_i)。
- 共会执行(q)次操作,每次随机选择一个区间,让区间中的所有数都成为这个区间的最大值。
- 问数组每个位置最终元素值的期望。
- (n,qle400)
震惊,没想到居然能自己推出这道题来!
枚举最终值
考虑既然是求期望,我们不如枚举(x),去计算第(p)个位置最终值为(x)的方案数。
恰为(x)不好求,一个简单的容斥就是变成求最终值小于等于(x)的方案数(G_{p,x}),那么实际方案数就是(G_{p,x}-G_{p,x-1})。
此时,我们就可以把所有小于等于(x)的数视作(0),大于(x)的数视作(1)。
一开始序列中可能会有若干(1),把序列分成了很多小段。而由于(1)是不可能消掉的,所以小段之间是相互独立的。
那么我们不妨设(f_{i,j,k})表示(i)次操作之后,([j,k])中的数都是(0),且(f_{j-1}=f_{k+1}=1)的方案数。
转移时总共会有三种可能的情况:
- 当前修改区间不会影响到([j,k])中的值。那么就是在([1,j),[j,k],(k,n])中某一块内选择一个区间的方案数。
- 一段前缀被修改为(1)。则枚举原本的左端点(t),有(f_{i,j,k}=sum_{t=1}^{j-1}f_{i,t,k} imes(t-1)),显然可以前缀和优化转移。
- 一段后缀被修改为(1)。则枚举原本的右端点(t),有(f_{i,j,k}=sum_{t=k+1}^nf_{i,j,t} imes(n-t)),类似地可以后缀和优化转移。
于是我们已经有了一个(O(n^4))的做法了。
最终贡献化数组初值
我们发现对于不同的(x),(DP)转移的过程是完全一样的。
用(s)表示(a)排序后的结果,考虑最终答案的计算,就是(sum_{i=1}^ns_i(G_{p,s_i}-G_{p,s_{i-1}}))。
稍微变个形就是(sum_{i=1}^n(s_i-s_{i+1})G_{p,s_i})。
考虑原先对于(x)的(DP)的初始状态,本应是给满足(a_{jsim k})都(le x)且(a_{j-1}>x,a_{k+1}>x)的区间(DP)初值(f_{0,j,k})赋为(1)。
因此现在我们只要先对于每个(x),给满足(a_{jsim k})都(le x)且(a_{j-1}>x,a_{k+1}>x)的区间(DP)初值(f_{0,j,k})加上(s_i-s_{i+1}),再一起(DP)一遍就好了。
代码:(O(n^3))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 400
#define X 1000000007
#define S(x) ((x)*((x)+1)/2)
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,m,a[N+5],s[N+5],f[2][N+5][N+5];
int main()
{
RI i,j,k;for(scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",a+i),s[i]=a[i];
for(a[0]=a[n+1]=1e9,sort(s+1,s+n+1),i=1;i<=n;++i) for(j=1;j<=n;++j) if(a[j-1]>s[i])
for(k=j;k<=n&&a[k]<=s[i];++k) if(a[k+1]>s[i]) f[0][j][k]=(f[0][j][k]+s[i]-s[i+1]+X)%X;//初始化
RI o,t;for(i=o=1;i<=m;++i,o^=1)//动态规划
{
for(j=1;j<=n;++j) for(k=j;k<=n;++k) f[o][j][k]=1LL*f[o^1][j][k]*(S(j-1)+S(n-k)+S(k-j+1))%X;//不变
for(k=1;k<=n;++k) for(t=0,j=1;j<=k;++j) Inc(f[o][j][k],t),t=(1LL*f[o^1][j][k]*(j-1)+t)%X;//变左端点
for(j=1;j<=n;++j) for(t=0,k=n;k>=j;--k) Inc(f[o][j][k],t),t=(1LL*f[o^1][j][k]*(n-k)+t)%X;//变右端点
}
for(i=1;i<=n;printf("%d%c",t,"
"[i==n]),++i)//对每个位置,统计所有包含该位置的区间的答案
for(t=0,j=1;j<=i;++j) for(k=i;k<=n;++k) Inc(t,f[m&1][j][k]);return 0;
}