这题简直鬼畜= =
题意:给出一个序列,m次操作,每次随机取一个区间,将这个区间里面的数变为这个区间内的最大数字。输出每个数字的期望值*((n(n+1))/2)^q.
对于这种期望题目,如果不能直接算期望,那肯定是把除数拿出来然后直接算。
这里我们要算出每个数能变成什么数的方案数,那么明显dp。
设
g[i][j]
表示第i个数变为排名第j的数的方案数。
那么我们枚举j,假设rank[j]=x,那么
g[i][j]>0
的时候肯定是在区间l,r内,满足
a[l]...a[r]<=x
.
然后我们来膜一发lych的题解。。
如果令f[k][x][y]表示经过k轮后,恰好是[x,y]这个范围内的数都变成了从小到大第j个数的方案数。但是这样会存在问题,就是如果某一轮的操作跨过了l或r,就会造成[l,r]中某一些数>从小到大第j个数,这样再转移就会出错。
所以令f[k][x][y]表示经过k轮后,恰好是[x,y]范围内的数都变成了小于a[rank[j]]的方案数。
那么有三种转移:
1.f[k][x][y]由f[k][x][y+1…n]转移而来。
f[k][x][y]+=∑r=y+1nf[k][x][r]∗(n−r)
2.f[k][x][y]由f[k][1…x-1][y]转移而来
基本同上。
3.由[u,v]转移而来,[u,v]与[x,y]无交集
那么直接预处理这种就好。
s[i][j]=calc(i−1)+calc(j−i+1)+calc(n−j)
代码挺短,但是巨慢= =
30s我居然30.4s没T,也是神奇= =
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=5e2+5;
const int mo=1e9+7;
int n,m;
typedef long long ll;
int f[2][N][N],g[N][N],s[N][N];
int a[N],b[N],c[N],rank[N],l,r,tmp,ans;
bool cmp(int x,int y)
{
return a[x]<a[y];
}
inline int calc(int x)
{
return x*(x+1)/2;
}
inline void solve(int l,int r,int p)
{
int last,x=0,tmp;
fo(i,l,r)
fo(j,i,r)f[0][i][j]=0;
f[0][l][r]=1;
fo(k,1,m)
{
last=x;
x^=1;
fo(i,l,r)
{
tmp=0;
fd(j,r,i)
{
f[x][i][j]=tmp;
tmp=(tmp+1ll*f[last][i][j]*(n-j))%mo;
}
}
fo(i,l,r)c[i]=0;
fo(i,l,r)
{
fo(j,i,r)
{
f[x][i][j]=(f[x][i][j]+c[j])%mo;
c[j]=(c[j]+1ll*f[last][i][j]*(i-1))%mo;
}
}
fo(i,l,r)
fo(j,i,r)
f[x][i][j]=(f[x][i][j]+1ll*f[last][i][j]*s[i][j])%mo;
}
fo(i,l,r)
{
tmp=0;
fd(j,r,i)
{
tmp=(tmp+f[x][i][j])%mo;
g[j][rank[p]]=(g[j][rank[p]]+tmp)%mo;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
fo(i,1,n)
{
scanf("%d",&a[i]);
b[i]=i;
}
sort(b+1,b+1+n,cmp);
fo(i,1,n)rank[b[i]]=i;
fo(i,1,n)
{
fo(j,1,n)
s[i][j]=calc(i-1)+calc(n-j)+calc(j-i+1);
}
fo(i,1,n)
{
l=r=i;
while (l>1&&a[l-1]<a[i])l--;
while (r<n&&a[r+1]<a[i])r++;
solve(l,r,i);
}
fo(i,1,n)
{
tmp=ans=0;
fo(j,1,n)if (g[i][j])
{
g[i][j]-=tmp;
if (g[i][j]<0)g[i][j]+=mo;
ans=(ans+1ll*g[i][j]*a[b[j]])%mo;
tmp=(tmp+g[i][j])%mo;
}
printf("%d%c",ans,(i<n)?' ':'\n');
}
}