Luogu P5355 [Ynoi2017] 由乃的玉米田 题解

洛谷 P5355 [Ynoi2017] 由乃的玉米田

人生中第一道 Ynoi,多少得写点什么纪念一下。

题目链接

解析:

这道题算是P3674 小清新人渣的本愿的加强版,所以我们仍然可以使用类似的套路。

对于 12 操作,利用 bitset 来维护序列中数的出现情况,3 操作直接枚举因数,不会的可以看我的这篇博客

主要的问题是 4 操作,我们先考虑暴力枚举,设 d=1,使得 d×xmax{ai},再判断 dd×xbitset 中是否出现过。
这样的复杂度是 O(max{ai}x) 的,可近似看做 O(Nx) 的,其中 N 为数据的上限。

不难发现,当 x 越大时,枚举的次数越少。考虑根号分治,设定一个阀值 S=N,当 xS 时,dN,可以保证复杂度最大是 O(N) 的;当 x<S 时,我们不用莫队,单独处理。

考虑如何单独处理,对于每一个 x,从 1n 遍历一遍 ai,用 preai 表示 ai 最近一次出现的位置。

之后进行一个类似扫描线的操作。设序列中存在两个数 ab,满足 ab 的商为 x,即 b=a÷xb=a×x。用 resi 记录从位置 1i 能找到的 b 的最靠右(最大)的位置。

然后我们考虑如何求出这个 res 数组。既然说过了是一个类似扫描线的操作,那我们不妨设这根“线”为 l,其记录着最近的 b 的位置 。resi 要么来源于 ai,要么直接继承之前的 l。所以,当 preai×xpreai÷x>l 时,更新 l,最后的 l 即为 resi 的值。

设询问的左端点为 ql,右端点为 qr,若 qlresqr,表示询问的区间内满足有两数的商为 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;
}
posted @   TSTYFST  阅读(110)  评论(9编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示