• 【BZOJ4574】[ZJOI2016] 线段树(动态规划)


    点此看题面

    • 给定一个长度为(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;
    }
    
  • 相关阅读:
    Selenium python 常用定位方法
    第四周总结
    python自然语言处理——提取关键词,标签
    python 调用百度地图api接口获取地理详细信息行政代码等
    python分词技术——jieba安装使用
    质量属性战术--6.易用性战术
    Kettle的使用——大数据清洗技术
    周总结2
    DataX的使用——大数据同步技术
    python编程:从入门到实践
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4574.html
Copyright © 2020-2023  润新知