正题
题目链接:https://www.luogu.com.cn/problem/P3352
题目大意
nnn个数字的一个序列,每次随机选择一个区间让这个区间所有数等于这个区间的最大值,重复qqq次,对每个位置求所有情况下这个位置的值的和。
1≤n,q≤4001\leq n,q\leq 4001≤n,q≤400,保证数据随机
解题思路
设fk,l,rf_{k,l,r}fk,l,r表示使用了kkk次目前覆盖了极大区间l,rl,rl,r时的方案。
这个极大区间就是无法继续向左右扩展(就是左右两边是边界或者比这个区间内所有数都大),不然相同的方案会统计入不同的数组导致算重。
然后每次我们找一个数字开始向左右扩展到极大区间进行dpdpdp,然后dpdpdp方程是
fk,l,r=fk−1,l,r×gl,r+∑i=Ll−1fk−1,i,r+∑i=r+1Rfk−1,l,i+1f_{k,l,r}=f_{k-1,l,r}\times g_{l,r}+\sum_{i=L}^{l-1}f_{k-1,i,r}+\sum_{i=r+1}^{R}f_{k-1,l,i+1}fk,l,r=fk−1,l,r×gl,r+i=L∑l−1fk−1,i,r+i=r+1∑Rfk−1,l,i+1
也就是固定端点的情况下扩展极大区间,因为是反过来的所以这样是对的。
然后记录一个dpdpdp数组ansi,jans_{i,j}ansi,j表示数字iii至少为第jjj小的情况数,这个每次dpdpdp后都可以统计。
上面每个dpdpdp区间相当于笛卡尔树上的区间,因为数据随机,所以每个位置只会计算logloglog次。
时间复杂度O(nq2+n3)O(nq^2+n^3)O(nq2+n3)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=410,P=1e9+7;
ll n,q,a[N],b[N],rk[N],f[2][N][N],ans[N][N],cnt[N];
void solve(ll x,ll L,ll R){for(ll i=L;i<=R;i++)for(ll j=i;j<=R;j++)f[0][i][j]=f[1][i][j]=0;f[0][L][R]=1;for(ll k=1;k<=q;k++){for(ll i=L;i<=R;i++)for(ll j=i;j<=R;j++)f[k&1][i][j]=f[~k&1][i][j]*(cnt[j-i+1]+cnt[i-1]+cnt[n-j]);for(ll i=L;i<=R;i++){ll buf=0;for(ll j=R;j>=i;j--){(f[k&1][i][j]+=buf)%=P;(buf+=f[~k&1][i][j]*(n-j))%=P;}}for(ll j=L;j<=R;j++){ll buf=0;for(ll i=L;i<=j;i++){(f[k&1][i][j]+=buf)%=P;(buf+=f[~k&1][i][j]*(i-1))%=P;}}}for(ll i=L;i<=R;i++){ll buf=0;for(ll j=R;j>=i;j--){(buf+=f[q&1][i][j])%=P;(ans[j][rk[x]]+=buf)%=P;}}return;
}
signed main()
{scanf("%lld%lld",&n,&q);for(ll i=1;i<=n;i++)cnt[i]=i*(i+1)/2; for(ll i=1;i<=n;i++){scanf("%lld",&a[i]);b[i]=a[i];}sort(b+1,b+1+n);ll m=unique(b+1,b+1+n)-b-1;for(ll i=1;i<=n;i++)rk[i]=lower_bound(b+1,b+1+m,a[i])-b;for(ll i=1;i<=n;i++){ll L=i,R=i;while(L>1&&a[L-1]<a[i])L--;while(R<n&&a[R+1]<a[i])R++;solve(i,L,R);}for(ll i=1;i<=n;i++){ll sum=0;for(ll j=1;j<=n;j++){if(!ans[i][j]){continue;}for(ll k=1;k<j;k++)(ans[i][j]+=P-ans[i][k])%=P;(sum+=ans[i][j]*b[j]%P)%=P;}printf("%lld ",sum);}return 0;
}