P5355 [Ynoi2017] 由乃的玉米田

传送门


思路

经典的 bitset 优化莫队

先考虑减法:由 \(a-b=x\) 可得 \(a=b+x\),那么我们用 bitset 记录对应的数字是否出现过,然后在询问时,我们将 bitset 整体向左移 \(x\) 位,再与原 bitset 取交集,如果不为 \(0\),显然是可行的

然后加法与减法类似:考虑 \(a=Mx-a'\),那么有 \(a-b'=a+b-Mx=x-Mx\),移项后 \(a+Mx-x=b'\),那我们就再用一个 bitset 维护 \(Mx-a\),询问时与减法相同

乘法我们直接暴力枚举 \(\sqrt x\) 个因子进行判断即可,复杂度与莫队的复杂度是同阶的

除法倒是有些棘手,我们考虑用根号分治:

  • \(x\ge \sqrt {Mx}\)\(Mx\) 是最大的数),那么我们直接像乘法那样暴力枚举就行了

  • \(x< \sqrt{Mx}\),我们考虑不使用莫队;我们对每种询问 \(x\) 进行一次扫描线:\(la[num]\) 记录 \(num\) 目前出现最靠右的位置,\(Rpos[i]\) 记录扫到 \(i\) 时,满足存在两个数商为 \(x\)(没有余数)的最右左端点;流程如下:当扫到 \(i\) 时,将 \(la[a[i]]=i\)\(Rpos[i]=max(la[a[i]*x], la[a[i]/x],Rpos[i-1])\);处理询问时,我们只需要比较 \(ql\)(询问区间的左端点)是否小于等于 \(Rpos[qr]\) 即可


代码

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
inline int reads()
{
    int sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
int n, m, len, Mx, Mxa, sq, a[100005], cl[100005];
struct Node
{
    int op, l, r, x, id;
}q[100005];
inline bool cmp(Node a, Node b) {return cl[a.l] ^ cl[b.l] ? a.l < b.l : a.r < b.r;}
bool ans[100005]; int nl = 1, nr, cnt[100005]; std::bitset<100005> f, g;
inline void add(int pos) {cnt[a[pos]]++; f[a[pos]] = g[Mx - a[pos]] = 1;}
inline void del(int pos) {cnt[a[pos]]--; if(!cnt[a[pos]]) f[a[pos]] = g[Mx - a[pos]] = 0;}
std::vector<int> ask[320]; int la[100005], Rpos[100005];
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = reads(), m = reads(); len = sqrt(n);
    for(int i = 1; i <= n; i++) a[i] = reads(), cl[i] = (i - 1) / len + 1, Mx = std::max(Mx, a[i]);
    Mxa = Mx; sq = sqrt(Mxa);
    for(int i = 1; i <= m; i++)
        q[i] = (Node){reads(), reads(), reads(), reads(), i}, Mx = std::max(Mx, q[i].x);
    std::sort(q + 1, q + 1 + m, cmp);
    for(int i = 1; i <= m; i++)
    {
        while(q[i].l < nl) add(--nl);
        while(q[i].r > nr) add(++nr);
        while(q[i].l > nl) del(nl++);
        while(q[i].r < nr) del(nr--);
        if(q[i].op == 2) ans[q[i].id] = (g & (f << (Mx -  q[i].x))) != 0;
        else if(q[i].op == 1) ans[q[i].id] = (f & (f << q[i].x)) != 0;
        else if(q[i].op == 3)
        {
            for(int j = 1; j * j <= q[i].x; j++)
                if(!(q[i].x % j) && f[j] && f[q[i].x / j])
                {
                    ans[q[i].id] = true;
                    break;
                }
        }
        else if(q[i].x >= sq)
        {
            for(int j = 1; j * q[i].x <= Mxa; j++)
                if(f[j] && f[j * q[i].x])
                {
                    ans[q[i].id] = true;
                    break;
                }
        }
        else ask[q[i].x].emplace_back(i);
    }
    for(int i = 1; i < sq; i++)
    {
        if(ask[i].empty()) continue;
        for(int j = 1; j <= n; j++)
        {
            la[a[j]] = j;
            Rpos[j] = Rpos[j - 1];
            if(a[j] * i <= Mxa) Rpos[j] = std::max(Rpos[j], la[a[j] * i]);
            if(!(a[j] % i)) Rpos[j] = std::max(Rpos[j], la[a[j] / i]);
        }
        for(const auto& id : ask[i]) ans[q[id].id] = (q[id].l <= Rpos[q[id].r]);
        memset(la, 0, sizeof(la)), memset(Rpos, 0, sizeof(Rpos));
    }
        
    for(int i = 1; i <= m; i++)
        if(ans[i]) printf("yuno\n");
        else printf("yumi\n");
    return 0;
}
posted @ 2022-04-19 14:34  zuytong  阅读(35)  评论(0编辑  收藏  举报