[bzoj4574][UOJ#196][ZJOI2016]线段树

题目大意

一个随机的序列长度为n。
有m次操作,每次随机选择一个区间[l,r],将这个区间所有数变成最大值。
问每个位置在m次操作后的期望。
答案模1e9+7
n<=400,权值随机

DP

权值随机的条件可以认为权值互不相同
离散化
我们设sum[i][j]表示第i个位置变成第j小的权值的方案数。
我们一个数一个数的做,设当前做的数是第now小,权值为val[now]。
我们设g[k,i,j]表示k次操作后一个极大区间[i,j]满足区间内每个数都不超过val[now],因为是极大所以如果存在位置i-1和j+1,一定有i-1和j+1位置上的权重比val[now]大。
我们思考怎么转移。
k次操作后极大区间是[i,j],那么k-1次操作时极大区间有几种可能:
1、是[u,j], u<i ,那么我们肯定是选择了一个小于u的位置w,然后操作了[w,i-1],才会使得极大区间变成[i,j]。
2、是[i,v],基本同上。
3、就是[i,j],那么我们选择的操作区间要么被[i,j]包含,要么与[i,j]不相交。
就三种转移的情况,dp即可。
最后统计答案的时候,把<=j变成=j即可。注意dp数组为0的位置表示该情况不可能出现。
需要卡常,我卡过了UOJ没卡过bzoj。。
代码的很多实现其实是抄的QAQ

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define dp(i,j,k) dp[(i)%2][(j)][(k)]
using namespace std;
typedef long long ll;
const int maxn=400+10,mo=1000000007;
int L[maxn],R[maxn],a[maxn],b[maxn][maxn],rank[maxn];
ll dp[2][maxn][maxn],sum[maxn][maxn];
int sta[80],cnt[maxn],val[maxn];
int i,j,k,l,t,n,m,ans,top;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void solve(int l,int r,int now){
    int i,j,k;
    ll t;
    fo(i,l,r)
        fo(j,l,r)
            dp(0,i,j)=dp(1,i,j)=0;
    dp(0,l,r)=1;
    fo(k,1,m){
        fo(j,l,r){
            t=0;
            fo(i,l,j){
                dp(k,i,j)=t;
                t+=(ll)dp(k-1,i,j)*(i-1)%mo;
            }
        }
        fo(i,l,r){
            t=0;
            fd(j,r,i){
                (dp(k,i,j)+=t)%=mo;
                t+=(ll)dp(k-1,i,j)*(n-j)%mo;
            }
        }
        fo(i,l,r)
            fo(j,i,r)
                dp(k,i,j)=(dp(k,i,j)+dp(k-1,i,j)*b[i][j]%mo)%mo;
    }
    fo(i,l,r){
        t=0;
        fd(j,r,i){
            t+=dp(m,i,j);
            (sum[j][rank[now]]+=t)%=mo;
        }
    }
}
void write(int x){
    if (!x){
        putchar('0');
        //putchar(' ');
        return;
    }
    top=0;
    while (x){
        sta[++top]=x%10;
        x/=10;
    }
    while (top) putchar('0'+sta[top--]);
    //putchar(' ');
}
int main(){
    //freopen("data.in","r",stdin);
    n=read();m=read();
    fo(i,0,n) cnt[i]=i*(i+1)/2;
    fo(i,1,n)
        fo(j,i,n)
            b[i][j]=((ll)cnt[i-1]+(ll)cnt[n-j]+(ll)cnt[j-i+1])%mo;
    fo(i,1,n) a[i]=read(),val[i]=a[i];
    sort(val+1,val+n+1);
    fo(i,1,n) rank[i]=a[i]=lower_bound(val+1,val+n+1,a[i])-val;
    fo(i,1,n){
        j=i-1;
        while (j&&a[j]<=a[i]) j=L[j]-1;
        L[i]=j+1;
    }
    fd(i,n,1){
        j=i+1;
        while (j<=n&&a[j]<=a[i]) j=R[j]+1;
        R[i]=j-1;
    }
    fo(i,1,n) solve(L[i],R[i],i);
    fo(i,1,n){
        ans=0;t=0;
        fo(j,1,n){
            if (!sum[i][j]) continue;
            sum[i][j]=(sum[i][j]-t)%mo;
            t=(t+sum[i][j])%mo;
            ans=(ans+(ll)sum[i][j]*val[j]%mo)%mo;
        }
        (ans+=mo)%=mo;
        write(ans);
        if (i<n) putchar(' ');
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值