bzoj4574 [Zjoi2016]线段树 DP

来源:互联网 发布:画结构式的软件 编辑:程序博客网 时间:2024/06/05 16:05

这题简直鬼畜= =
题意:给出一个序列,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](nr)

2.f[k][x][y]由f[k][1…x-1][y]转移而来
基本同上。
3.由[u,v]转移而来,[u,v]与[x,y]无交集
那么直接预处理这种就好。
s[i][j]=calc(i1)+calc(ji+1)+calc(nj)
代码挺短,但是巨慢= =
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');    }}