Vexoben
Mar 19, 2019
题目链接:ZJOI2016 线段树
(刷新以获取数学公式)
题意
给定一个长为的随机序列(即每个元素在范围内随机),次操作,每次随机选定一个区间,将这段区间中的数都替换为这段区间的最大值。求最终每个数的期望乘
数据范围:
题解
相当于计算在所有可能的操作下,每个数最终结果之和。于是我们只需要求出最终变成每个数的方案数。
由于元素在内随机,我们可以认为所有数互不相同。对于每个数,我们可以处理出一个区间,使得它是这个区间的最大值(也就是建出笛卡尔树)。
设计一个状态:设当前枚举数是,令为进行了次操作是一个最大值不超过的极大区间的方案数。极大的意思是,如果,那么上的数必须大于。显然只有当的时候状态是有意义的。
这样转移就很显然了:
第一个是选择一个的子区间或一个和没有交集的区间进行操作,从转移。
第二个是原来极大区间是,选择一个左端点在,右端点在的区间进行操作。第三个和第二个类似。
计算答案是枚举这个数不大于多少,然后枚举极大区间即可。
这里认为同阶。在数据非随机的情况下复杂度是。但是数据随机可以证明期望是
事实上我们只要证明:对长度为的序列建立的笛卡尔树,所有节点控制区间的长度平方和是
不妨设这个值是,枚举最大值的位置,那么:
令
#pragma GCC optimize("2,Ofast,inline")
#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define LL long long
#define Ldb long double
#define pii pair<int, int>
using namespace std;
const int N = 405;
const int mod = 1e9 + 7;
template <typename T> void read(T &x) {
int f = 0;
char c = getchar();
while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
for (x = 0; c >= '0' && c <= '9'; c = getchar())
x = (x << 3) + (x << 1) + (c ^ '0');
if (f) x = -x;
}
int n, m;
int a[N], val[N];
int f[2][N][N], sum[N][N];
LL calc(int x) {
return 1LL * x * (x + 1) / 2 % mod;
}
void upd(int &x, int y) {
x = (x + y >= mod) ? x + y - mod : x + y;
}
void solve(int x, int L, int R) {
for (int i = L; i <= R; ++i) {
for (int j = i; j <= R; ++j) {
f[0][i][j] = f[1][i][j] = 0;
}
}
f[0][L][R] = 1;
int p = 1;
for (int k = 1; k <= m; ++k, p ^= 1) {
for (int i = L; i <= R; ++i) {
for (int j = i; j <= R; ++j) {
f[p][i][j] = 0;
}
}
for (int i = L; i <= R; ++i) {
for (int j = i; j <= R; ++j) {
int s = 1LL * f[p ^ 1][i][j];
s = 1LL * s * (calc(i - 1) + calc(n - j) + calc(j - i + 1)) % mod;
upd(f[p][i][j], s);
}
}
for (int j = L; j <= R; ++j) {
int t = 0;
for (int i = L; i <= j; ++i) {
upd(f[p][i][j], t);
upd(t, 1LL * (i - 1) * f[p ^ 1][i][j] % mod);
}
}
for (int i = L; i <= R; ++i) {
int t = 0;
for (int j = R; j >= i; --j) {
upd(f[p][i][j], t);
upd(t, 1LL * (n - j) * f[p ^ 1][i][j] % mod);
}
}
}
for (int i = L; i <= R; ++i) {
for (int j = i; j <= R; ++j) {
for (int k = i; k <= j; ++k) {
upd(sum[k][x], f[p ^ 1][i][j]);
}
}
}
}
int main() {
read(n); read(m);
for (int i = 1; i <= n; ++i) {
read(a[i]);
val[i] = a[i];
}
sort(val + 1, val + n + 1);
int tot = unique(val + 1, val + n + 1) - val - 1;
for (int i = 1; i <= n; ++i) {
a[i] = lower_bound(val + 1, val + tot + 1, a[i]) - val;
}
for (int i = 1; i <= n; ++i) {
int L = i, R = i;
while (L > 1 && a[L - 1] < a[i]) --L;
while (R < n && a[R + 1] < a[i]) ++R;
solve(a[i], L, R);
}
for (int i = 1; i <= n; ++i) {
int ans = 0;
for (int j = 1; j <= n; ++j) {
int k = sum[i][j];
if (!k) {
sum[i][j] = sum[i][j - 1];
continue;
}
upd(k, mod - sum[i][j - 1]);
upd(ans, 1LL * val[j] * k % mod);
}
if (ans < 0) ans += mod;
printf("%d ", ans);
}
puts("");
}