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";
}
本文来自博客园,作者:CuteNess,转载请注明原文链接:https://www.cnblogs.com/CuteNess/p/18841626

浙公网安备 33010602011771号