【YBT2023寒假Day4 C】樱桃莓莓(交互)(四毛子分块)(线段树)

樱桃莓莓

题目链接:YBT2023寒假Day4 C

题目大意

有一个黑盒操作满足交换律和结合律,有 n 个数,q 次询问,每次选 m 个下标,你要计算所有不包含那 m 个下标的数进行黑盒操作之后的结果。
预处理不超过 4n 次,每次询问不超过 4(m+1) 次。(这里的一次是指使用一次黑盒操作)

思路

第一步想到的应该是前缀后缀,但是中间的要怎么办呢。
考虑分块(有点四毛子的感觉),求出每一块的前缀后缀。
但是发现怎么都还是会超 4n,那这个时候考虑不要了整个的前缀后缀,思考超 4n 的原因。

就是一个块 [x,y] 内如果 [x+1,y1] 问这个就寄了。
(块长小的话要拼的也太多了)
那考虑每个块中间你在弄一个点,从这个点往前和往后在处理一遍。
(那现在是 3n

先看看够不够,发现当 B=8 的时候,如果小块的询问没有跨越中间的部分,那它长度至多是 4,我们就可以暴力一个一个点并起来查询。
那小块就可以了,接着搞大块,考虑怎么办。

试着能不能用线段树的结构,会发现好像如果要这样的话,你合并的时候时间又超了。
考虑分摊一下,分摊到预处理那里,于是考虑线段树每个位置你都处理一个前缀后缀。
然后到询问第一次裂成两半的时候,你就可以直接分别用那两半的后缀和前缀直接拼出来。

于是数一数行不行。
如果整个询问是小块,至多是 4 可以。
如果询问分成两个小块和大段,每个小块由于分出来的一定是前缀或者后缀,大段我们用线段树的的方法至多 2 次,也是 4
然后看初始化,前面的是 3n,考虑给线段树那个部分是多少。
注意线段树是把段分线段树的,所以总的是 2n/8logn/8,会发现超了。

不过其实这个 2 可以去掉,因为一个线段树上的点要么是只用前缀,要么是只用后缀(根据它下标的奇偶,1 就不会有)
所以就是 n/8logn/8,大概是 1901 左右当 n=2000,就 <n,所以是可以的。

代码

#include <algorithm> #include <vector> #include <cstdio> #include <set> #include "blackbox.h" #define ull unsigned long long using namespace std; const int N = 10000; int n, m, B = 8, blo[N], bl[N], br[N]; ull pre[N], suf[N], Mid[N], b[N]; struct XD_tree { ull f[N][N]; void build(int now, int l, int r) { if (now != 1) { if (now & 1) { f[now][l] = pre[br[l]]; for (int j = l + 1; j <= r; j++) f[now][j] = magic(f[now][j - 1], pre[br[j]]); } else { f[now][r] = pre[br[r]]; for (int j = r - 1; j >= l; j--) f[now][j] = magic(f[now][j + 1], pre[br[j]]); } } if (l == r) return; int mid = (l + r) >> 1; build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r); } ull query(int now, int l, int r, int L, int R) { if (L <= l && r <= R) { if (now & 1) return f[now][r]; else return f[now][l]; } int mid = (l + r) >> 1; if (L <= mid && mid < R) return magic(f[now << 1][L], f[now << 1 | 1][R]); if (L <= mid) return query(now << 1, l, mid, L, R); if (mid < R) return query(now << 1 | 1, mid + 1, r, L, R); } }T; void init(vector <ull> a, int M) { n = a.size(); m = M; for (int i = 1; i <= n; i++) b[i] = a[i - 1], blo[i] = (i - 1) / B + 1; for (int i = 1; i <= blo[n]; i++) { bl[i] = (i - 1) * B + 1; br[i] = min(n, i * B); int l = bl[i], r = br[i]; pre[l] = b[l]; for (int j = l + 1; j <= r; j++) pre[j] = magic(pre[j - 1], b[j]); suf[r] = b[r]; for (int j = r - 1; j >= l; j--) suf[j] = magic(suf[j + 1], b[j]); int mid = min(n, l + 3); Mid[mid] = b[mid]; for (int j = mid - 1; j >= l; j--) Mid[j] = magic(Mid[j + 1], b[j]); if (mid != r) { Mid[mid + 1] = b[mid + 1]; for (int j = mid + 2; j <= r; j++) Mid[j] = magic(Mid[j - 1], b[j]); } } T.build(1, 1, blo[n]); } ull slove(int l, int r) { if (blo[l] == blo[r]) { int mid = blo[l] + 3; if (r - l + 1 > 4) return magic(Mid[l], Mid[r]); ull re = b[l]; for (int i = l + 1; i <= r; i++) re = magic(re, b[i]); return re; } ull re = magic(suf[l], pre[r]); if (blo[l] != blo[r] - 1) re = magic(re, T.query(1, 1, blo[n], blo[l] + 1, blo[r] - 1)); return re; } ull query(vector <int> u) { sort(u.begin(), u.end()); ull ans = 0; int las = 0, nof = 0; for (int i = 0; i < m; i++) { int l = las + 1, r = u[i]; if (l <= r) { if (!nof) nof = 1, ans = slove(l, r); else ans = magic(ans, slove(l, r)); } las = u[i] + 1; } if (las != n) { if (!nof) ans = slove(las + 1, n); else ans = magic(ans, slove(las + 1, n)); } return ans; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT2023Day4_C.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2021-02-03 【POJ 1148】Utopia Divided
点击右上角即可分享
微信分享提示