第六场春训,谢谢zlc学长 qwq!
(E题很有意思哦)
【BUAA Spring Training 06】
Tags:二分图匹配 2-SAT 笛卡尔树 基环外向树 斯坦纳树+状压
Problem B. 满意度期望
[B] 题意
对于一个序列
A={a1, a2, a3,... an}
\text{A=\{a1, a2, a3,... an\}}
A={a1, a2, a3,... an} ,定义
P(A, l, r)
\text{P(A, l, r)}
P(A, l, r) 为
{al,... ar}
\text{\{al,... ar\}}
{al,... ar} 中最大数的最左位置。
定义两个序列
A
\text{A}
A 和
B
\text{B}
B 相似,当且仅当
∀
l
,
r
∈
[
1
,
n
]
s
.
t
.
P(A, l, r) = P(B, l, r)
\forall\ l, r \in [1, n] \ \ s.t.\ \ \text{P(A, l, r) = P(B, l, r)}
∀ l,r∈[1,n] s.t. P(A, l, r) = P(B, l, r)。
现给定一个有 n \text{n} n 个元素的序列 A \text{A} A ,再随机产生一个也有 n \text{n} n 个元素的序列 B \text{B} B,其中 B \text{B} B 中的所有元素都是从 (0, 1) \text{(0, 1)} (0, 1) 区间内随机选择的。如果 A \text{A} A 和 B \text{B} B 是相似的,那么 B \text{B} B 的"满意度"为 B \text{B} B 中所有值的和,否则为 0 0 0。求 B \text{B} B 的“满意度”的期望。
[B] 思路
学长分享的经验是,遇见这种区间最值的位置关系,就可以考虑用 笛卡尔树 来做。
笛卡尔树是什么?笛卡尔树是由某个序列生成的二叉树,其中序遍历就是原序列。其每棵子树满足:根结点是子树的最值。
是不是想到了 Treap ?没错,当区间树使的 FHQ Treap \text{FHQ Treap} FHQ Treap 就是一棵笛卡尔树。
那么对于本题我们可以发现,两个序列 A \text{A} A 和 B \text{B} B 是相似的,当且仅当由他们生成的笛卡尔树的 “形态” 是相同的。或者说把数给离散化之后再建笛卡尔树,两棵树相同(两棵树每个结点的子树大小对应相等)则原序列相似。
然后再回到本题。首先 B \text{B} B 数组必定没有相等元素(连续区间上选有限个数,显然选到相等两个数的概率为 0 0 0),然后要求 B \text{B} B 和 A \text{A} A 相似,那么对于每棵笛卡尔树的子树对应的那个小区间 [ l e f t , r i g h t ] [left, right] [left,right],必须要指定某个位置最大。这样的概率显然是 1 r i g h t − l e f t + 1 \frac{1}{right-left+1} right−left+11 的,或者说 1 s i z e \frac{1}{size} size1 的。
这样 B \text{B} B 和 A \text{A} A 相似的概率就是 A \text{A} A 笛卡尔树的每棵子树的 s i z e size size 的倒数的乘积 Π \Pi Π。
而显然 B \text{B} B 数组的每个元素之和的期望是 n × n \times n× 0 + 1 2 \frac{0+1}{2} 20+1 = = == == n 2 \frac{n}{2} 2n。
所以满意度期望就是 n Π 2 \frac{n\Pi}{2} 2nΠ。
时间复杂度:预处理逆元和建树都是 O ( n ) O(n) O(n) 的。
[B] 代码
/*
* If we give,
* all we've got,
* we will make it through.
*/
#include <cstdio>
#include <climits>
#include <cstring>
#define GC getchar()
#define _SN(x) {char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define _SAN(a,n) {auto _i=0,_n=n;for(;_i<_n;++_i)_SN(a[_i])}
#define _SA(a,l,r) {auto _i=l,_r=r;for(;_i<_r;++_i)_SN(a[_i])}
#define _gS(_1, _2, _3, _sc, ...) _sc
#define sc(...) _gS(__VA_ARGS__,_SA,_SAN,_SN, ...)(__VA_ARGS__)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define _F0N(i,n) for(auto i=0;i<(n);++i)
#define _FLR(i,l,r) for(auto i=(l);i<(r);++i)
#define _gF(_1, _2, _3, _F, ...) _F
#define F(...) _gF(__VA_ARGS__,_FLR,_F0N, ...)(__VA_ARGS__)
#define _FD0(i,n) for(auto i=0;i<=(n);++i)
#define _FDL(i,l,r) for(auto i=(l);i<=(r);++i)
#define _gFD(_1, _2, _3, _FD, ...) _FD
#define FD(...) _gFD(__VA_ARGS__,_FDL,_FD0, ...)(__VA_ARGS__)
#define LL long long
#define ULL unsigned LL
#define PC putchar
template<class T>
void UPRT(const T _){if(_>=10)UPRT(_/10);PC(_%10+48);}
#define Tjj int T;sc(T)while(T--)
const int MN = 3e6+7;
const LL MOD = 1000000007;
LL inv[MN];
int a[MN];
int sta[MN]; // "指针"数组
int lc[MN], rc[MN];
int build(const int n)
{
memset(lc, 0, sizeof(*lc) * (n+1));
memset(rc, 0, sizeof(*rc) * (n+1));
int top = 0;
sta[top] = 0; // 哨兵指针取成没用的0
a[sta[top]] = INT_MAX; // 哨兵的值,极大。(这和大根堆Treap建树是一回事。小根堆哨兵用极小,大根堆哨兵用极大)
FD(i, 1, n)
{
while (a[sta[top]] < a[i]) // 和小根堆Treap建树相反,这里是栈顶的函值小于当前值,而不是大于
lc[i] = sta[top--];
rc[sta[top]] = i;
sta[++top] = i;
}
return rc[0]; // 和Treap建树一样,最后的root是哨兵的右指针
}
LL ans;
int sz[MN];
void dfs(const int u)
{
sz[u] = 1;
if (lc[u])
dfs(lc[u]), sz[u] += sz[lc[u]];
if (rc[u])
dfs(rc[u]), sz[u] += sz[rc[u]];
ans *= inv[sz[u]];
ans %= MOD;
}
int main()
{
inv[1] = 1;
F(i, 2, MN)
inv[i] = inv[MOD%i] * (MOD-MOD/i) % MOD;
Tjj
{
get(n)
sc(a, 1, n+1)
const int ROOT = build(n);
ans = n * inv[2] % MOD;
dfs(ROOT);
UPRT(ans), PC(10);
}
return 0;
}
Problem B.
(待更)