• @loj



    @description@

    小 Yuuka 遇到了一个题目:有一个序列 a1,a2,...,an,对其进行q次操作,每次把一个区间内的数改成区间内的最大值,问最后每个数是多少。

    小 Yuuka 很快地就使用了线段树解决了这个问题。于是充满智慧的小 Yuuka 想,如果操作是随机的,即在这 q 次操作中每次等概率随机地选择一个区间 [l,r] (1<=l<=r<=n),然后将这个区间内的数改成区间内最大值(注意这样的区间共有 n*(n+1)/2 个),最后每个数的期望大小是多少呢?
    小 Yuuka 非常热爱随机,所以她给出的输入序列也是随机的(随机方式见数据规模和约定)。对于每个数,输出它的期望乘 (n*(n+1)/2)^q 再对 10^9 + 7 取模的值。

    输入格式
    第一行包含两个正整数 n,q,表示序列里数的个数和操作的个数。

    接下来一行,包含 n 个非负整数 a1,a2,...,an。

    输出格式
    输出共一行,包含 n 个整数,表示每个数的答案

    样例输入
    5 5
    1 5 2 3 4
    样例输出
    3152671 3796875 3692207 3623487 3515626

    数据范围与提示
    对于所有的测试数据,保证序列中数的大小不超过 10^9,即 ai<=10^9,并且每个数是 0 到 10^9 之间的随机整数。n<=400, q<=400。

    @solution@

    直接计算不太方便,我们转换一下问题。
    记 g(x, i) 表示第 i 个位置最终 <= x 的方案数,则得到第 i 个位置的最终期望为:

    [ans_i = sum_{x=0}x*(g(x, i) - g(x-1, i)) ]

    记 mx = max{a[1...n]}。因为当 x > max{a} 的时候有 g(x, i) = g(x-1, i),所以我们可以取上界:

    [ans_i = sum_{x=0}^{mx}x*(g(x, i) - g(x-1, i)) = mx*g(mx, i) - sum_{x=0}^{mx-1}g(x, i) ]

    假如要求解 g(x, i),我们可以令设状态 f(x, l, r, p),表示第 p 步后 a[l...r] <= x 且 a[l-1] > x, a[r+1] > x 的方案数。注意必须两个条件同时满足,否则就不好弄。
    于是 (g(x, i) = sum_{l=1}^{i}sum_{r=i}^{n} f(x, l, r, q))

    怎么转移呢?考虑第 p + 1 步的操作区间是什么样子:
    (1)操作区间完全包含于 [1, l-1],或 [l, r],或 [r+1, n]。这时这个操作对这个区间无影响,从 f(x, l, r, p) 转移到 f(x, l, r, p+1)。可以预处理一下转移系数。
    (2)操作区间包含 [l, r] 且不等于 [l, r],显然这个区间就没了,无法转移。
    (3)操作区间与 [l, r] 不完全相交。不妨假设操作区间在左边,另一边同理。设操作区间为 [l1, r1] 且 l1 < l <= r1 < r,则可以从 f(x, l, r, p) 转移到 f(x, r1 + 1, r, q)。这个转移可以前缀和搞定。

    初始时,对于每一个 i 求区间 [li, ri] 使得 a[li...ri] <= a[i] 且 a[li-1] > x, a[ri+1] > x(这个区间是唯一的),令 f(x, li, ri, 0) = 1。
    然后呢?其实这个时候可以直接写了(因为数列全部是随机,所以期望跑得很快)。但是我们还可以更优。

    注意到转移与 x 无关,x 只与初始状态有关。假如令 f(mx, 1, n, 0) = -mx,则我们完全可以把 x 这一维去掉(但是我们还要考虑没有在序列中出现的权值)。
    用一下滚动数组就可以 O(n^3) 轻松过这道题了。

    @accepted code@

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int INF = (1<<30);
    const int MOD = int(1E9) + 7;
    const int MAXN = 400;
    inline int add(int x, int y) {return (x + y)%MOD;}
    inline int mul(int x, int y) {return 1LL*x*y%MOD;}
    inline int sub(int x, int y) {return add(x,MOD-y);}
    inline int func(int x) {return 1LL*x*(x-1)/2%MOD;}
    int trans[MAXN + 5][MAXN + 5];
    int f[2][MAXN + 5][MAXN + 5];
    int a[MAXN + 5];
    int n, q;
    int main() {
    	scanf("%d%d", &n, &q);
    	for(int i=1;i<=n;i++)
    		scanf("%d", &a[i]);
    	a[0] = a[n + 1] = INF;
    	for(int i=1;i<=n;i++) {
    		int p = a[i], q;
    		for(int j=i;j<=n;j++) {
    			p = max(p, a[j]), q = min(a[i-1], a[j+1]);
    			if( i == 1 && j == n ) f[0][i][j] = MOD - p;
    			else f[0][i][j] = max(0, q - p);
    			trans[i][j] = add(func(j-i+2), add(func(i), func(n-j+1)));
    		}
    	}
    	for(int i=1;i<=q;i++) {
    		for(int j=1;j<=n;j++)
    			for(int k=j;k<=n;k++)
    				f[1][j][k] = f[0][j][k], f[0][j][k] = mul(trans[j][k], f[1][j][k]);
    		for(int k=1;k<=n;k++) {
    			int tmp = 0;
    			for(int j=1;j<=k;j++) {
    				f[0][j][k] = add(f[0][j][k], tmp);
    				tmp = add(tmp, mul(f[1][j][k], j-1));
    			}
    		}
    		for(int j=n;j>=1;j--) {
    			int tmp = 0;
    			for(int k=n;k>=j;k--) {
    				f[0][j][k] = add(f[0][j][k], tmp);
    				tmp = add(tmp, mul(f[1][j][k], n-k));
    			}
    		}
    	}
    	for(int i=1;i<=n;i++) {
    		int ans = 0;
    		for(int j=1;j<=i;j++)
    			for(int k=i;k<=n;k++)
    				ans = add(ans, f[0][j][k]);
    		printf("%d%c", sub(MOD, ans), i == n ? '
    ' : ' ');
    	}
    }
    

    @details@

    ZJOI 的题都是神仙题 * 4。

    主要难点在于设计状态与状态优化。

  • 相关阅读:
    object-c iOS 教程 git for mac
    mac Git本地服务器配置
    [转]XCode中修改缺省公司名称/开发人员名称
    IOS------Warning
    Linux---CentOS 定时运行脚本配置练手
    微信公众号一些错误的原因错误代码41001
    微信支付的一些新的经验总结
    关于THINKPHP5模型关联的初步理解
    写下thinkphp5和thinkphp3.2的不同
    练手THINKPHP5过程和bootstrap3.3.7
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11653774.html
Copyright © 2020-2023  润新知