1. 程式人生 > 其它 >P5355 [Ynoi2017] 由乃的玉米田

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;
}