912 字
5 分钟
题解 CF2211F Learning Binary Search
前置
二分搜索树
把二分搜索的过程建成一棵二叉树即可. 此时每个节点对应唯一一个点mid, 也可以认为每个节点对应唯一一个二分搜索区间[l, r]. 二分搜索树共有n个节点.
范德蒙德恒等式
见”组合数学”板块. 我们有
曲棍球棒
题意
给定一个函数如下:
function f(a, k, l, r): if a does not contain k: return 0 mid = floor((l+r) / 2) if a[mid]==k: return 1 else if a[mid]<k: return 1+f(a, k, mid+1, r) else: return 1+f(a, k, l, mid-1)对于所有的单调不减的且的序列和, 求和, 模.
解法
求和对象的改变
容易发现, 该函数即为二分搜索找到的次数. 所以我们可以建立二分搜索树, 对于每个节点, 考虑有多少个使得搜索在此处停止, 再乘以该节点的深度即可.
注意点1
注意到会被访问, 当且仅当原数组中所有k都被包含在中. 证明好证, 主要是难想到. 因为如果k被分割成两段, 那在分割点就已经返回了, 不会到下面. 同时, 注意到搜索在处停止, 当且仅当. 也就是说, 当数组中占据的区间为时, 要求且.
考虑容斥原理
先考虑这个条件, 实际上要求. 他的种类数是(1)
如果, 我们需要减去的情况, 由于mid处已经是k了, 的情况说明且. 这种情况的种类数是(2):
时, 右边同理(3):
如果且, 我们还需要加上(4):
利用范德蒙德卷积进行变换
我们考虑对于所有的对上述式子求和. 利用范德蒙德卷积
(1)式为:
(2)式为:
(3)式为:
(4)式为:
最后, 对于每个节点使用容斥统计即可.
代码
void solution() { /* code here */ ll n, m; cin >> n >> m; mint ret = 0; auto dfs = [&](auto&& self, ll l, ll r, int d) -> void { if (l > r) { return; } ll mid = (l + r) / 2; ret += f.nCr(n + m - 1, m - 1) * d; if (l == 1 && r == n) { // mid - 1个位置填 [1, k] // n - mid个位置填 [k, m] } else if (l == 1) { ret -= f.nCr(mid + n - r + m - 2, m - 1) * d; } else if (r == n) { ret -= f.nCr(l - 2 + n - mid + m, m - 1) * d; } else { ret -= f.nCr(l - 2 + n - mid + m, m - 1) * d; ret -= f.nCr(mid + n - r + m - 2, m - 1) * d; ret += f.nCr(l + n - r + m - 3, m - 1) * d; } self(self, l, mid - 1, d + 1); self(self, mid + 1, r, d + 1); }; dfs(dfs, 1, n, 1); cout << ret.val << '\n';} 题解 CF2211F Learning Binary Search
https://fuwari.vercel.app/posts/题解-cf2211f-learning-binary-search/