Luogu P5355 [Ynoi2017] 由乃的玉米田 题解
洛谷 P5355 [Ynoi2017] 由乃的玉米田
人生中第一道 Ynoi,多少得写点什么纪念一下。
题目链接
解析:
这道题算是P3674 小清新人渣的本愿的加强版,所以我们仍然可以使用类似的套路。
对于 \(1\),\(2\) 操作,利用 bitset
来维护序列中数的出现情况,\(3\) 操作直接枚举因数,不会的可以看我的这篇博客。
主要的问题是 \(4\) 操作,我们先考虑暴力枚举,设 \(d = 1\),使得 \(d \times x ≤ \max\left \{ a_{i}\right \}\),再判断 \(d\) 和 \(d \times x\) 在 bitset
中是否出现过。
这样的复杂度是 \(O(\left \lfloor \frac{\max\left \{ a_{i} \right \}}{x} \right \rfloor)\) 的,可近似看做 \(O(\left \lfloor \frac{N}{x} \right \rfloor)\) 的,其中 \(N\) 为数据的上限。
不难发现,当 \(x\) 越大时,枚举的次数越少。考虑根号分治,设定一个阀值 \(S = \sqrt{N}\),当 \(x ≥ S\) 时,\(d ≤ \sqrt{N}\),可以保证复杂度最大是 \(O(\sqrt{N})\) 的;当 \(x < S\) 时,我们不用莫队,单独处理。
考虑如何单独处理,对于每一个 \(x\),从 \(1\) 到 \(n\) 遍历一遍 \(a_{i}\),用 \(pre_{a_{i}}\) 表示 \(a_{i}\) 最近一次出现的位置。
之后进行一个类似扫描线的操作。设序列中存在两个数 \(a\) 和 \(b\),满足 \(a,b\) 的商为 \(x\),即 \(b = a \div x\) 或 \(b = a \times x\)。用 \(res_{i}\) 记录从位置 \(1\) 到 \(i\) 能找到的 \(b\) 的最靠右(最大)的位置。
然后我们考虑如何求出这个 \(res\) 数组。既然说过了是一个类似扫描线的操作,那我们不妨设这根“线”为 \(l\),其记录着最近的 \(b\) 的位置 。\(res_{i}\) 要么来源于 \(a_{i}\),要么直接继承之前的 \(l\)。所以,当 \(pre_{a_{i} \times x}\) 或 \(pre_{a_{i} \div x} > l\) 时,更新 \(l\),最后的 \(l\) 即为 \(res_{i}\) 的值。
设询问的左端点为 \(ql\),右端点为 \(qr\),若 \(ql ≤ res_{qr}\),表示询问的区间内满足有两数的商为 \(x\)。
Code
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1e5 + 10, N = MAXN, SQ = 300;
int n, m, siz, lim, tot;
int num[MAXN], ans[MAXN];
int pre[MAXN], res[MAXN];
int belong[MAXN], temp[MAXN];
bitset<MAXN> add, cut;
struct Question{
int opt, l, r, x, id;
}q[MAXN];
vector<Question> p[MAXN];
bool cmp(const Question &a, const Question &b){
if(belong[a.l] != belong[b.l]) return belong[a.l] < belong[b.l];
if(belong[a.l] & 1) return a.r < b.r;
return a.r > b.r;
}
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
void Add(int x){
++temp[x];
if(temp[x] == 1){
add[x] = 1;
cut[N - x] = 1;
}
}
void Del(int x){
--temp[x];
if(temp[x] == 0){
add[x] = 0;
cut[N - x] = 0;
}
}
bool Check_Add(int x){
return (add & (add << x)).any();
}
bool Check_Cut(int x){
return (cut & (add << (N - x))).any();
}
bool Check_Mul(int x){
for(register int d = 1; d * d <= x; d++){
if(x % d != 0) continue;
if(add[d] && add[x / d]) return true;
}
return false;
}
bool Check_Div(int x){
for(register int d = 1; d * x <= MAXN; d++)
if(add[d] && add[x * d]) return true;
return false;
}
void Modify(int l, int r){
for(register int i = 1; i <= tot; i++){
int opt = q[i].opt, x = q[i].x;
while(l < q[i].l) Del(num[l++]);
while(l > q[i].l) Add(num[--l]);
while(r < q[i].r) Add(num[++r]);
while(r > q[i].r) Del(num[r--]);
if(opt == 1) ans[q[i].id] = Check_Add(x);
else if(opt == 2) ans[q[i].id] = Check_Cut(x);
else if(opt == 3) ans[q[i].id] = Check_Mul(x);
else if(opt == 4) ans[q[i].id] = Check_Div(x);
}
}
void init(){
for(register int x = 1; x <= SQ; x++){
if(p[x].empty()) continue;
int l = 0;
memset(pre, 0, sizeof(pre));
for(register int i = 1; i <= n; i++){
pre[num[i]] = i;
if(num[i] * x <= MAXN) l = max(l, pre[num[i] * x]);
if(num[i] % x == 0) l = max(l, pre[num[i] / x]);
res[i] = l;
}
for(register int i = 0; i < p[x].size(); i++){
if(p[x][i].l <= res[p[x][i].r]) ans[p[x][i].id] = 1;
else ans[p[x][i].id] = 0;
}
}
}
int main(){
n = read(), m = read();
for(register int i = 1; i <= n; i++){
num[i] = read();
lim = max(lim, num[i]);
}
siz = sqrt(n);
for(register int i = 1; i <= n; i++)
belong[i] = (i - 1) / siz + 1;
for(register int i = 1; i <= m; i++){
int opt, l, r, x;
opt = read(), l = read(), r = read(), x = read();
if(opt == 4 && x <= SQ) p[x].push_back((Question){opt, l, r, x, i});
else q[++tot] = (Question){opt, l, r, x, i};
}
init();
sort(q + 1, q + 1 + tot, cmp);
Modify(1, 0);
for(register int i = 1; i <= m; i++){
if(ans[i]) puts("yuno");
else puts("yumi");
}
return 0;
}
以下为博客签名,与博文无关。
只要你们不停下来,那前面就一定有我。所以啊,不要停下来~
本文来自博客园,作者:TSTYFST,转载请注明原文链接:https://www.cnblogs.com/TSTYFST/p/16709749.html