• [ZJOI2016]线段树


    [ZJOI2016]线段树

    给定 (n) 个数 (a_{1,2...n}),每次随机操作一个区间,即将这个区间 ([l,r]) 的数变成这个区间的 (max)

    操作 (Q) 次,输出最后每个元素的权值的期望。

    (n,Qle 400)

    Solution

    期望的套路是差分贡献,当然直接计数也可以这样处理,所以不妨枚举 (x),设 (f_{l,r,k}) 表示当前操作了 (k) 次,区间 ([l,r]) 均小于 (x),同时 (a_{l-1},a_{r+1}) 均大于等于 (x) 的方案数。(当然小于等于的位置设为 (0),其他位置设为 (1)),那么答案即为 (max - sum f_{...} imes (w_x-w_{x-1}))

    然后考虑转移:

    [f_{l,r,k}=f_{l,r,k-1} imes q(l,r)+sum_{j< l} f_{j,r,k-1}(j-1)+sum_{j> r}f_{l,j,k-1}(n-j) ]

    其中 (q(l,r)) 表示无用操作的数量。

    考虑前缀和数组 (S_1(l,r,k),S_2(l,r,k)),分别维护一下即可,复杂度为 (mathcal O(n^3q))

    可以考虑将初始值设为 (w_x-w_{x-1}),这样就不需要将答案乘以 (w_x) 了。

    考虑优化到 (mathcal O(n^3)),不难发现对于不同的 (x),转移只有初值不同,所以对每个区间预处理初值和即可。

    注意到 ([l,r]) 的初值即 (min(a_{l-1},a_{r+1})-max(a_{l,l+1...r})),于是复杂度 (mathcal O(n^2q))

    (Code:)

    #include<bits/stdc++.h>
    using namespace std ;
    #define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
    #define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
    #define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
    #define re register
    #define F(x) ((x) * (x + 1) / 2) 
    #define int long long
    int gi() {
    	char cc = getchar() ; int cn = 0, flus = 1 ;
    	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
    	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
    	return cn * flus ;
    }
    const int N = 400 + 5 ; 
    const int P = 1e9 + 7 ; 
    int n, q, a[N], mx[N][N], f[N][N][2], S1[N][N], S2[N][N] ; 
    int w(int l, int r) {
    	return F(l - 1) + F(n - r) + F(r - l + 1) ; 
    }
    signed main()
    {
    	n = gi(), q = gi() ; 
    	rep( i, 1, n ) a[i] = gi() ; 
    	int mi = a[1] ;
    	rep( i, 1, n ) mi = min( mi, a[i] ) ;
    	rep( l, 1, n ) rep( r, l, n ) mx[l][r] = max( mx[l][r - 1], a[r] ) ; 
    	rep( l, 1, n ) rep( r, l, n ) {
    		if( min( a[l - 1], a[r + 1] ) > mx[l][r] )
    			f[l][r][0] = min( a[l - 1], a[r + 1] ) - mx[l][r] ; 
    		if( l == 1 && a[r + 1] > mx[l][r] ) 
    			f[l][r][0] = a[r + 1] - mx[l][r] ; 
    		if( r == n && a[l - 1] > mx[l][r] ) 
    			f[l][r][0] = a[l - 1] - mx[l][r] ; 
    	}
    	f[1][n][0] = 0 ;
    	rep( r, 1, n ) rep( l, 1, r ) S1[l][r] = (S1[l - 1][r] + f[l][r][0] * (l - 1) ) % P ;
    	rep( l, 1, n ) drep( r, l, n ) S2[l][r] = (S2[l][r + 1] + f[l][r][0] * (n - r) ) % P ; 
    	rep( u, 1, q ) {
    		int k = u & 1 ; 
    		rep( l, 1, n ) rep( r, l, n ) 
    		f[l][r][k] = (f[l][r][k ^ 1] * w(l, r) % P + S1[l - 1][r] + S2[l][r + 1] ) % P ; 
    		rep( r, 1, n ) rep( l, 1, r ) S1[l][r] = (S1[l - 1][r] + f[l][r][k] * (l - 1) ) % P ;
    		rep( l, 1, n ) drep( r, l, n ) S2[l][r] = (S2[l][r + 1] + f[l][r][k] * (n - r) ) % P ; 
    	}
    	int p = n * (n + 1) / 2, D = 1 ; 
    	rep( i, 1, q ) D = D * p % P ; q = q & 1 ; 
    	rep( i, 1, n ) {
    		int ans = mx[1][n] * D % P ; 
    		rep( l, 1, n ) rep( r, l, n ) if( l <= i && i <= r ) ans = (ans - f[l][r][q] + P) % P ; 
    		printf("%lld ", ans ) ; 
    	}
    	return 0 ;
    }
    
  • 相关阅读:
    vscode中设置Python解释器
    Python语言中当前工作目录(Current Working Directory, cwd)与模块搜索第一路径都是指什么???
    在vscode中通过修改launch.json文件为项目添加环境变量——在launch.json文件中修改env变量
    Docker 中安装的 RabbitMQ 开通STOMP通道 用于 WebSocket 的Web端连接
    初中生能看懂的微积分
    conda创建虚拟环境到指定文件夹
    Sorry, Ubuntu 16.04 has experienced an internal error
    卷积
    Mac mini和Mac studio
    linux下无法进入外加移动硬盘文件夹 No such file or directory
  • 原文地址:https://www.cnblogs.com/Soulist/p/13675016.html
Copyright © 2020-2023  润新知