P7811 你的名字

你的名字我怎么还没看过这个动漫啊(

长为 \(n\) 的序列,\(m\) 次询问 \(min_{l\le i\le r} a_i \bmod k\) 的最小值。

\(n,m\le 3\times 10^5,k,V\le10^5\)\(V\) 是值域。


发现跟取模有关,果断考虑根号分治。

定一个阈值 \(T\),对于 \(k\le T\) 的询问,直接在 \(a_i \bmod k\) 的序列上维护最小值,使用分块可以做到预处理 \(O(nT)\),询问 \(O(\sqrt{n})\)

对于 \(k > T\) 的询问, 可以发现 \(\dfrac{n}{k} \le \dfrac{n}{T}\),我们考虑枚举 \(k\) 的每个倍数 \(ck\),此时原问题的最小值相当于对于个每个 \(ck\) 的满足 \(a_i\ge ck\)\(a_i-ck\) 的最小值。

我们把所有的 \(ck\) 都离线下来,从大到小扫描值域并插入数,那么我们就只需要维护最小值就可以了,但是由于 \(ck\) 最多有 \(\dfrac{nV}{T}\) 个,我们可能需要做到 \(O(1)\) 查询。

不过这个是简单的,我们对序列分块,块内维护前后缀最小值,\(O(\sqrt n)\) 更新,块之间维护 ST 表,每次暴力更新,复杂度也是 \(O(\sqrt n)\),那么我们就获得了(预处理?)\(O(n\sqrt n)\),每次查询 \(O(\dfrac{nV}{T})\) 的方法。

根号平衡一下,\(T=\sqrt V\) 时最优,复杂度 \(O((n+m)\sqrt n+n\sqrt V)\)


#include <algorithm>
#include <iostream>
#include <vector>
#include <math.h>
#include <tuple>
#include <cstring>

using std::cin, std::cout, std::cerr;
const int N = 3e5 + 7, K = 557*1.4, V = 1e5, INF = 2e9;

#define R(i,a,b) for(int i(a);i<=(b);++i)
#define P(i,a,b) for(int i(a);i<(b);++i)
#define len(x) ((int)x.size())

int n, m, v[N];

int B, blockLen, lp[K], rp[K], id[N];
void Init() {
    blockLen = sqrt(n)/1.4; 
    B = n / blockLen, rp[B] = n;
    P(i, 1, B) rp[i] = i * blockLen;
    R(i, 1, B) lp[i] = rp[i-1] + 1;
    R(i, 1, B) R(j, lp[i], rp[i]) id[j] = i;
}

auto upd = [](auto& x, auto&& y) {x = std::min(x, y);};

namespace _Less {
    const int sq = 600;
    int min[K], need[sq];
    std::vector<std::tuple<int, int, int*>> req[N];

    inline int query(int x, int y, int k) {
        int res = INF;
        R(i, x, rp[id[x]]) upd(res, v[i] %k);
        R(i, id[x]+1, id[y]-1) upd(res, min[i]);
        R(i, lp[id[y]], y) upd(res, v[i] %k);
        return res;
    }

    void work() {
        for(int k = 1; k < sq; ++k) {
            if(!need[k]) continue;
            R(i, 1, B) min[i] = INF;
            R(i, 1, n) upd(min[id[i]], v[i] %k);
            for(auto& [x, y, p]: req[k]) {
                *p = query(x, y, k);
            }
        }
    }

};

namespace _Greater {
    const int LK = std::__lg(K) + 1;
    int pre[N], suf[N], tab[LK][K], Log[N];

    std::vector<std::tuple<int, int, int*>> req[V+7];
    std::vector<int> pos[V+7];

    inline void Init() { 
        Log[1] = 0;
        P(i, 2, N) Log[i] = Log[i>>1] + 1;
        memset(pre, 0x3f, sizeof pre);
        memset(suf, 0x3f, sizeof suf);
        memset(tab, 0x3f, sizeof tab);
    }

    inline void Modify(int x, int y) {
        R(i, x, rp[id[x]]) pre[i] = y;
        R(i, lp[id[x]], x) suf[i] = y;
        x = id[x];
        tab[0][x] = y;
        P(j, 1, LK) {
            int r = std::min(x + (1 << j) - 1, B);
            R(i, std::max(x, 1<<j), r) tab[j][i] = y;
        }
    }

    inline int _query(int x, int y) {
        int k = Log[y - x + 1];
        return std::min(tab[k][y], tab[k][x+(1<<k)-1]);
    }

    inline int query(int x, int y) {
        int res = std::min(suf[x], pre[y]);
        if(id[y] > id[x] + 1) 
            upd(res, _query(id[x] + 1, id[y] - 1));
        return res;
    }

    inline void work() {
        auto work = [](int j, int i) {
            if(req[j].empty()) return ;
            for(auto& [x, y, p]: req[j]) {
                upd(*p, query(x, y) - i);
            }
        };

        R(i, 1, n) pos[v[i]].push_back(i);
        for(int i = V; i > 0; --i) {
            for(auto& p: pos[i]) Modify(p, i);
            for(int j = 1; j * j <= i; ++j) {
                if(i % j == 0) {
                    work(j, i);
                    if(i / j != j) work(i/j, i);
                }
            }
        }
        for(int j = 1; j <= V; ++j) work(j, 0);
    }
};

int ans[N];

signed main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    cin >> n >> m;
    R(i, 1, n) cin >> v[i];
    Init();

    R(i, 1, m) {
        ans[i] = INF;
        int x, y, k;
        cin >> x >> y >> k;
        if(y - x <= blockLen) {
            int res = INF;
            R(i, x, y) upd(res, v[i] %k);
            ans[i] = res;
        } else if(k < _Less::sq) {
            _Less::need[k] = 1;
            _Less::req[k].emplace_back(x, y, ans+i);
        } else {
            _Greater::req[k].emplace_back(x, y, ans+i);
        }
    }

    _Greater::Init();
    _Greater::work();
    _Less::work();

    R(i, 1, m) cout << ans[i] << "\n"; 
}
posted @ 2025-04-22 21:20  CuteNess  阅读(24)  评论(0)    收藏  举报