@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@

  1. #include<cstdio>
  2. #include<algorithm>
  3. using namespace std;
  4. const int INF = (1<<30);
  5. const int MOD = int(1E9) + 7;
  6. const int MAXN = 400;
  7. inline int add(int x, int y) {return (x + y)%MOD;}
  8. inline int mul(int x, int y) {return 1LL*x*y%MOD;}
  9. inline int sub(int x, int y) {return add(x,MOD-y);}
  10. inline int func(int x) {return 1LL*x*(x-1)/2%MOD;}
  11. int trans[MAXN + 5][MAXN + 5];
  12. int f[2][MAXN + 5][MAXN + 5];
  13. int a[MAXN + 5];
  14. int n, q;
  15. int main() {
  16. scanf("%d%d", &n, &q);
  17. for(int i=1;i<=n;i++)
  18. scanf("%d", &a[i]);
  19. a[0] = a[n + 1] = INF;
  20. for(int i=1;i<=n;i++) {
  21. int p = a[i], q;
  22. for(int j=i;j<=n;j++) {
  23. p = max(p, a[j]), q = min(a[i-1], a[j+1]);
  24. if( i == 1 && j == n ) f[0][i][j] = MOD - p;
  25. else f[0][i][j] = max(0, q - p);
  26. trans[i][j] = add(func(j-i+2), add(func(i), func(n-j+1)));
  27. }
  28. }
  29. for(int i=1;i<=q;i++) {
  30. for(int j=1;j<=n;j++)
  31. for(int k=j;k<=n;k++)
  32. f[1][j][k] = f[0][j][k], f[0][j][k] = mul(trans[j][k], f[1][j][k]);
  33. for(int k=1;k<=n;k++) {
  34. int tmp = 0;
  35. for(int j=1;j<=k;j++) {
  36. f[0][j][k] = add(f[0][j][k], tmp);
  37. tmp = add(tmp, mul(f[1][j][k], j-1));
  38. }
  39. }
  40. for(int j=n;j>=1;j--) {
  41. int tmp = 0;
  42. for(int k=n;k>=j;k--) {
  43. f[0][j][k] = add(f[0][j][k], tmp);
  44. tmp = add(tmp, mul(f[1][j][k], n-k));
  45. }
  46. }
  47. }
  48. for(int i=1;i<=n;i++) {
  49. int ans = 0;
  50. for(int j=1;j<=i;j++)
  51. for(int k=i;k<=n;k++)
  52. ans = add(ans, f[0][j][k]);
  53. printf("%d%c", sub(MOD, ans), i == n ? '\n' : ' ');
  54. }
  55. }

@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. 彻底删除 Git 项目中的文件(BFG Repo-Cleaner 用法)

    一些时候由于开发初期经验不足和贪图方便, 会把一些不应该提交到 Git 的文件上传到 Github, 带来一系列安全问题, 更有可能是把一些大文件上传到 GitHub 上, 导致项目非常臃肿, 每次 ...

  2. android 复制到剪切板

    The Clipboard Framework 当使用clipboard framework时,把数据放在一个剪切对象(clip object)里,然后这个对象会放在系统的剪贴板里. clip obj ...

  3. JS中的$符号

    1. 首先可以用来表示变量, 比如变量 var s='asdsd'或var $s='asdasd'; 2. 在正则表达式中,它可以匹配结尾 /sa$/.test(string) 匹配string字符串 ...

  4. XML之基础和DTD解析

    本笔记可根据W3school教程学习: 首先-----了解XML文档结构.语法规范.作用 -----了解DTD约束的作用.具体约束语法 <?xml version="1.0" ...

  5. Socket与TcpClient的区别

    原文:Socket与TcpClient的区别 Socket和TcpClient有什么区别 原文:http://wxwinter.spaces.live.com/blog/cns!C36588978AF ...

  6. 【风马一族_win10设置热点】win10无法开启热点怎么办

    输入cmd,设置管理员开启 输入netsh wlan set hostednetwork mode=allow ssid=name(无线名字) key=00000000(8位以上密码); 输入nets ...

  7. Django与HTML业务基本结合

    app里都有migrations文件 app: migrations 数据修改表结构,数据库操作记录 admin Django为我们提供后台管理 apps 配置当前app models ORM,写指定 ...

  8. QT_强杀进程

    #ifdef WIN32 bool res = false; HANDLE hToolHelp32Snapshot; hToolHelp32Snapshot = CreateToolhelp32Sna ...

  9. 微信小程序--轮播图,标题,盒子,tab栏的合成例子

    小程序是什么? 微信小程序,是一种不需要下载安装即可使用的应用,用户扫一扫或搜一下即可打开应用,在微信-发现-小程序可打开应用. 一.小程序的样式编写: 目录结构: app.json { " ...

  10. 通过sql 向数据库插入多行语句

    我们知道通过insert into 表名(列名) values(值)是向表中插入一条语句,可是当我们需要向数据库插入多条语句时,应该怎么做呢? 可以通过如下格式的sql 语句来实现一次向数据库插入多行 ...