1 条题解
-
0
自动搬运
来自洛谷,原作者为

Ireliaღ
逝去的是刀锋,不变的是意志搬运于
2025-08-24 22:09:54,当前版本为作者最后更新于2020-06-12 20:19:50,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
一句话题意
给一个长度为序列,每次询问给定,问区间内是否能找出两个数,它们的差/和/积/商为。、询问次数、值域均不超过。
解法
首先,看到[Ynoi],并且没有强制在线,那么我们可以直接考虑离线算法。弱化版:=>传送门
莫队,同时维护两个大小为值域的
bitset,暂且叫它们bs1和bs2。如果在当前区间出现,令bs1[i]、bs2[100000 - i]都。对于减法操作,答案是
((bs1 << x) & bs1).any()对于加法操作,答案是
ans[qs[i].i] = ((bs1 << (100000 - x)) & bs2).any()对于乘法操作,可以直接暴力,令从遍历到,查询是否和同时存在,注意必须被整除。
前三种操作在P3674的各路大佬的题解中有详细的复杂度证明,这里就不证了,讲一下第四种操作。
值域,那么我们可以把作为阈值,按大小把第四类询问分成两类,设计两种算法。
对于的询问,我们可以直接枚举,令从遍历到,查询是否和是否同时存在。由于不低于,所以不超过。那么,在莫队处理好区间信息后,这次查询的复杂度是。
对于的询问,我们单独处理,不使用莫队。
枚举。对于每一个,我们令从扫到,同时维护两个数组和。代表扫到时,最后一次出现的位置,代表扫到时,满足在同时出现和,或同时存在和的的最靠右的位置。每扫到一个点,我们用更新,再用和更新当前的就可以了。
显然,利用刚才处理出的数组,对于为当前的所有询问,它的答案均可以求出,即判断是否。由于枚举了个,每次遍历了整个序列,所以这一类询问总复杂度是
代码如下
#include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; int n, m; int a[MAXN]; int block, blo[MAXN]; int ans[MAXN]; int limit = 300; struct Data{ int l, r, x, i, t; Data() {} Data(int l, int r, int x, int i, int t) : l(l), r(r), x(x), i(i), t(t) {} }; Data qs[MAXN]; int qcnt; int cnt[MAXN]; bitset<MAXN> bs1, bs2; int Comp(const Data &a, const Data &b) { if (blo[a.l] != blo[b.l]) return a.l < b.l; return (blo[a.l] & 1) ? a.r < b.r : a.r > b.r; } void Ins(int x) { if (!cnt[x]) bs1[x] = 1, bs2[100000 - x] = 1; cnt[x]++; } void Del(int x) { cnt[x]--; if (!cnt[x]) bs1[x] = 0, bs2[100000 - x] = 0; } int CalcMul(int x) { for (int i = 1; i * i <= x; i++) if (x % i == 0 && bs1[i] && bs1[x / i]) return 1; return 0; } int CalcDiv(int x) { for (int i = 1; i * x <= 100000; i++) if (bs1[i] && bs1[i * x]) return 1; return 0; } void CaptainMo() { sort(qs + 1, qs + qcnt + 1, Comp); int nl = 1, nr = 0; for (int i = 1; i <= qcnt; i++) { int l = qs[i].l, r = qs[i].r, x = qs[i].x; while (nr < r) Ins(a[++nr]); while (nr > r) Del(a[nr--]); while (nl > l) Ins(a[--nl]); while (nl < l) Del(a[nl++]); if (qs[i].t == 1) ans[qs[i].i] = ((bs1 << x) & bs1).any(); else if (qs[i].t == 2) ans[qs[i].i] = ((bs1 << (100000 - x)) & bs2).any(); else if (qs[i].t == 3) ans[qs[i].i] = CalcMul(x); else ans[qs[i].i] = CalcDiv(x); } } vector<int> ql[MAXN], qr[MAXN], qi[MAXN]; int pre[MAXN], maxl[MAXN]; void Solve() { for (int x = 1; x <= limit; x++) { if (ql[x].empty()) continue; int l = 0; for (int i = 1; i <= n; i++) { int y = a[i]; pre[y] = i; if (x * y <= 100000) l = max(l, pre[x * y]); if (y % x == 0) l = max(l, pre[y / x]); maxl[i] = l; } for (unsigned i = 0; i < ql[x].size(); i++) ans[qi[x][i]] = (ql[x][i] <= maxl[qr[x][i]]); memset(pre, 0, sizeof(pre)); memset(maxl, 0, sizeof(maxl)); } } int main() { scanf("%d%d", &n, &m); block = sqrt(n); for (int i = 1; i <= n; i++) blo[i] = (i - 1) / block + 1; for (int i = 1; i <= n; i++) scanf("%d", a + i); for (int i = 1; i <= m; i++) { int opt, l, r, x; scanf("%d%d%d%d", &opt, &l, &r, &x); if (opt == 4 && x <= limit) ql[x].push_back(l), qr[x].push_back(r), qi[x].push_back(i); else qs[++qcnt] = Data(l, r, x, i, opt); } CaptainMo(); Solve(); for (int i = 1; i <= m; i++) puts(ans[i] ? "yuno" : "yumi"); return 0; }
- 1
信息
- ID
- 4333
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者