@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 ? '\n' : ' ');
}
}

@details@

ZJOI 的题都是神仙题 * 4。

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

@loj - 2093@ 「ZJOI2016」线段树的更多相关文章

  1. Loj #2570. 「ZJOI2017」线段树

    Loj #2570. 「ZJOI2017」线段树 题目描述 线段树是九条可怜很喜欢的一个数据结构,它拥有着简单的结构.优秀的复杂度与强大的功能,因此可怜曾经花了很长时间研究线段树的一些性质. 最近可怜 ...

  2. @loj - 3043@「ZJOI2019」线段树

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 九条可怜是一个喜欢数据结构的女孩子,在常见的数据结构中,可怜最喜 ...

  3. 【LOJ】#3043. 「ZJOI2019」线段树

    LOJ#3043. 「ZJOI2019」线段树 计数转期望的一道好题-- 每个点设两个变量\(p,q\)表示这个点有\(p\)的概率有标记,有\(q\)的概率到祖先的路径上有个标记 被覆盖的点$0.5 ...

  4. 「模板」 线段树——区间乘 && 区间加 && 区间求和

    「模板」 线段树--区间乘 && 区间加 && 区间求和 原来的代码太恶心了,重贴一遍. #include <cstdio> int n,m; long l ...

  5. 「ZJOI2019」线段树 解题报告

    「ZJOI2019」线段树 听说有人喷这个题简单,然后我就跑去做,然后自闭感++,rp++(雾) 理性分析一波,可以发现最后形成的\(2^k\)个线段树,对应的操作的一个子集,按时间顺序作用到这颗线段 ...

  6. LOJ 3043: 洛谷 P5280: 「ZJOI2019」线段树

    题目传送门:LOJ #3043. 题意简述: 你需要模拟线段树的懒标记过程. 初始时有一棵什么标记都没有的 \(n\) 阶线段树. 每次修改会把当前所有的线段树复制一份,然后对于这些线段树实行一次区间 ...

  7. 「ZJOI2019」线段树

    传送门 Description 线段树的核心是懒标记,下面是一个带懒标记的线段树的伪代码,其中 tag 数组为懒标记: 其中函数\(Lson(Node)\)表示\(Node\)的左儿子,\(Rson( ...

  8. 【LOJ3043】「ZJOI2019」线段树

    题面 问题可以转化为每次区间覆盖操作有 \(\frac{1}{2}\) 的概率进行,求标记和的期望.于是我们只要求出所有点有标记的概率即可. 我们设 \(f_i\) 表示节点 \(i\) 有标记的概率 ...

  9. LOJ#2983. 「WC2019」数树

    传送门 抄题解 \(Task0\),随便做一下,设 \(cnt\) 为相同的边的个数,输出 \(y^{n-cnt}\) \(Task1\),给定其中一棵树 设初始答案为 \(y^n\),首先可以发现, ...

随机推荐

  1. [转]js设计模式-策略模式

    在程序设计中,常常遇到类似的情况,要实现某一个功能有多种方案可以选择.比如一个压缩文件的程序,既可以选择zip算法,也可以选择gzip算法.这些算法灵活多样,而且可以随意互相替换.这种解决方案就是本文 ...

  2. 20190722-Moni和Boly的故事

    Moni(模拟)可以得到60分 Boly(暴力)可以得到40分 还好说,这次有点可行. 过程是: 先看了T3,可以模拟,然后做T1T2 T1 好说,$exgcd$,不会. 暴力,暴力!骗了40. T2 ...

  3. 操作系统Lab1 详解(boot|kern/debug)

    总体 : boot kern libs tools boot asm.h bootmain.c bootasm.S asm.h 汇编头文件 SEG_NULLASM 定义一个空段描述符 SEG_ASM ...

  4. iOS -NSOperation并发编程

    http://www.cocoachina.com/game/20151201/14517.html http://blog.csdn.net/qinlicang/article/details/42 ...

  5. LINUX普通猫的拔号工具介绍

    普通猫分为串口和PCI的,请查看 <关于网络设备概述 > 普通猫的拔号工具主要有kppp和wvdial:在Redhat/Fedora中,用system-config-network 或re ...

  6. 【洛谷】P1554 梦中的统计

    P1554 梦中的统计 题目背景 Bessie 处于半梦半醒的状态.过了一会儿,她意识到她在数数,不能入睡. 题目描述 Bessie的大脑反应灵敏,仿佛真实地看到了她数过的一个又一个数.她开始注意每一 ...

  7. 【Ruby】与ruby相关的内容

    本博文是为了记录Ruby相关的可学习资源,以便日常使用 在线学习Rails的网站版本 Ruby on Rails教程(Rails 5) Ruby的中文社区 Ruby China

  8. mac Chrome 快捷键

    从windows迁移到mac,会发现快捷键真的变了,这里先吹一下mac OS天下第一 另外没有bar确实也提高了效率,这一点还是值的 其实程序员最常用的是检查,windows下是F12,默认F12会被 ...

  9. MVC开发模式与web经典三层框架

    MVC:Model(模型)-View(视图)-Controller(控制器) ----是一种软件架构模式,一般把软件系统拆分为这三个层次. 视图View层:前端交互界面或者后端系统界面,它从模型中获取 ...

  10. 【Pyqt5】之高级控件QTableWidget的使用

    1:动态渲染数据+动态添加控件(按钮,进度条) class MainWindow(QWidget, Ui_MainFrom): def __init__(self): super(MainWindow ...