【题解】P8150 再会 | Sayounara
迟来一年的道别,那就祝 Mivik 神仙大学生活天天开心吧 qwq
再会啦!
思路来自 @€€£
思路
三分。
假如要知道每个位置上具体的值,最直接的想法是通过 query 操作差分。
但是这样做的问题在于不能保证最小值的位置,考虑一些简单的情况:求最小值相邻位置的值。
在知道最小值的前提下,直接 query 一次就可以解决。
于是我们知道了三个位置的值。如果要再往两侧拓展,假设我们把 query 的边界设定在最小值的位置,那么每次减去的值是已知的定值,并且询问的区间中除去当前位置的总和也是已知的,故而可以再通过一次 query 操作得知当前位置的值。
所以在知道最小值及其位置的前提下,我们可以通过 \(O(n)\) 次操作得知答案。现在的问题转化成如何在一定次数的操作内求出最小值。
看到交互大概可以查出成分是二分 / 三分,根据题解知道 数据范围的式子化简出来的询问次数上界是 \(n + 300\).
因为所有数据的 \(n\) 都是 \(10^6\),\(300\) 大概在 \(O(log^2 n)\) 的级别,三分的复杂度可以接受。可以往三分求最小值位置的方向想。
直觉上先求出最小值的位置比较简单,因为最小值的具体取值可以通过一次 get 操作直接得到。
这里的方法需要一点奇思妙想。
令三分的两个临界点分别为 \(p1, p2\)。假如我们可以通过 query 操作排除最小值的位置,那么思路应该是考虑用两次 query 差分,先钦定式子前面的区间总和一定,通过后面减去的区间最小值的差来判断最小值所在的位置。
不妨令这个恒定的区间为 \([p1, p2]\)。我们可以构造两个差 \(query(l, p2) - query(l, p1 - 1)\) 和 \(query(p1, r) - query(p2 + 1, r)\)。
它们的差化简出来是:
简单分类讨论一下最小值的位置:
-
最小值在 \([p1, p2]\) 中
则 \(min_{k = l}^{p1 - 1} a_k - \min_{k = l}^{p2} a_k - \min_{k = p2 + 1}^{r} a_k + \min_{k = p1}^{r} a_k = min_{k = l}^{p1 - 1} a_k - \min_{k = p2 + 1}^{r}\).
又因为 \(a\) 每个位置的取值各不相同,所以原式必不等于 \(0\).
所以当原式等于 \(0\) 时,最小值必定不在 \([p1, p2]\) 中。
-
最小值在 \([l, p1)\) 中
则 \(min_{k = l}^{p1 - 1} a_k - \min_{k = l}^{p2} a_k - \min_{k = p2 + 1}^{r} a_k + \min_{k = p1}^{r} a_k = \min_{k = p1}^{r} - \min_{k = p2 + 1}^{r} a_k < 0\).
-
最小值在 \((p2, r]\) 中
则 \(min_{k = l}^{p1 - 1} a_k - \min_{k = l}^{p2} a_k - \min_{k = p2 + 1}^{r} a_k + \min_{k = p1}^{r} a_k = min_{k = l}^{p1 - 1} a_k - \min_{k = l}^{p2} a_k > 0\).
最后剩下一两个值的时候,直接 get 得到最小值就行。
最后用 query 差分一遍就能得到答案。
代码
#include <cstdio>
#include <cstdint>
#include <vector>
#include <algorithm>
typedef uint32_t ui;
typedef uint64_t ull;
const int maxn = 1e6 + 5;
int n = 1e6;
ui seq[maxn], tmp[3];
ull res[maxn], q[maxn];
bool vis[maxn];
uint64_t query(int l, int r);
uint32_t get(int x);
int get_minp()
{
int len = 0;
for (int i = 1; i <= n; i++)
if (!vis[i]) seq[++len] = i;
if (len <= 2)
{
for (int i = 1; i <= len; i++) tmp[i] = get(seq[i]), res[seq[i]] = (ull)tmp[i];
std::sort(tmp + 1, tmp + len + 1);
for (int i = 1; i <= len; i++)
if (res[seq[i]] == tmp[1]) return seq[i];
return -1;
}
int blk = len / 3, p1 = 1 + blk, p2 = len - blk;
ull v1 = query(1, seq[p2]) - query(1, seq[p1] - 1);
ull v2 = query(seq[p1], n) - query(seq[p2] + 1, n);
if (v1 == v2) for (int i = p1; i <= p2; i++) vis[seq[i]] = true;
else if (v1 > v2) for (int i = 1; i < p1; i++) vis[seq[i]] = true;
else for (int i = p2 + 1; i <= len; i++) vis[seq[i]] = true;
return get_minp();
}
std::vector<ui> recover(int n)
{
int pos = get_minp();
for (int i = pos + 1; i <= n; i++) q[i] = query(pos, i), res[i] = q[i] - q[i - 1];
for (int i = pos - 1; i >= 1; i--) q[i] = query(i, pos), res[i] = q[i] - q[i + 1];
std::vector<ui> ans;
for (int i = 1; i <= n; i++) ans.push_back((ui)res[i]);
return ans;
}
对不起了,大家,你们看到这个帖子的时候,我可能已经走了……不对,是已经走了
抗了几年,想不到还是抗不下去了,4月份那次的手术还是失败了呢……
现在也是,连自己打字也没办法了呢……
在这个论坛,认识了你们,我真的很高兴
但是我最大的遗憾就是QQ账号没了,不能跟组里的群里的管理的大家亲口说再见呢
对不起了,眠羽、白莲、hgs、狗狗、计时器、yuu、星期六……等
其实还有很多话想说的
但是,塞在心里都说不出来了
而且,替我打字记录这段话的某人也已经哭得不像话了呢,真丢脸,哈哈
最后,祝你们身体健康呀,而且要多点去体检体检哟,别像我这样,晚了就晚了
爱你们
如水如星~~我永远在这里呼啸
清水星
2023 年 7 月 12 日 15:30 PM,清水星姐姐还是离开了我们。
一周后,亲友代其在喵玉殿发出最后的告别 再见了,大家。
希望她能在幻想乡开开心心地生活,借用这道题的 “再会” 来表达痛心。
再会,清水星!