水题技巧之:如果你做莫队题时不会标准根号复杂度……
前情提要:教练墙了 luogu,但是 cnblogs 没封……
然后现在又把这个文章搬到了 luogu 上。
肯定有你不会的莫队题对吧,一定的有对么?然后呢,这篇文章可以让你把一些你不会做
所以以下均使用数列分块入门 9 作为例题。
正文
Part I:\log 的将就,O(n\sqrt m h)
一般我们看到一个用上了莫队但是又不太会 二次离线或看题解一个十分唐氏的东西:用
正如有人说过的:
所以复杂度注定不会非常好看(事实是难看到解不出来)。
至于思路,应该显然吧。
思考为什么分块经常和莫队搭档,因为人家可以
拿线段树类比,它的单次修改复杂度是
思考:为啥是
发现线段树维护信息的方式是“合并”+“上传”,只要你把一棵线段树上面砍掉,成为线段树森林,你的层数就会变小,至于查询?整棵线段树的查询显然是
然后你就可以微调
注意:如果你不会 ZKW 线段树的话最好别用这个办法,毕竟递归线段树的常数摆着呢,用了也难卡过,看看下一节吧。
code
zxkqwq 写的特别快,我就算了,懒得卡常。
// code by 樓影沫瞬_Hz17
#include <bits/stdc++.h>
#define en_ putchar_unlocked('\n')
#define e_ putchar_unlocked(' ')
// #define int long long
using namespace std;
inline int in() {
int n = 0; char p = getchar_unlocked();
while (p < '-') p = getchar_unlocked();
bool f = p == '-' ? p = getchar_unlocked() : 0;
do n = n * 10 + (p ^ 48), p = getchar_unlocked();
while (p >= '0');
return f ? -n : n;
}
inline int in(int &a) { return a = in(); }
inline void out(int n) {
if(n < 0) putchar_unlocked('-'), n = -n;
if(n > 9) out(n / 10);
putchar_unlocked(n % 10 + '0');
}
constexpr int N = 3e5 + 10, B = 580, K = 256;
using pii = pair<int, int>;
struct query {
int l, r, id;
} q[N];
int a[N], hs[N], pos[N], bel[N];
int L[N], R[N], cnt, n;
struct Tree {
uint mx[K * 2], id[K * 2];
#define ls (u << 1)
#define rs (u << 1 | 1)
inline void update(const int p) {
uint u = p - L[bel[p]] + K;
++ mx[u], id[u] = p;
for(u >>= 1; u; u >>= 1) {
if(mx[ls] >= mx[rs]) mx[u] = mx[ls], id[u] = id[ls];
else mx[u] = mx[rs], id[u] = id[rs];
}
}
inline void reduce(const int p) {
uint u = p - L[bel[p]] + K;
-- mx[u], id[u] = p;
for(u >>= 1; u; u >>= 1) {
if(mx[ls] >= mx[rs]) mx[u] = mx[ls], id[u] = id[ls];
else mx[u] = mx[rs], id[u] = id[rs];
}
}
} rt[N / K + 10];
int ans[N];
signed main() {
#ifndef ONLINE_JUDGE
freopen("in.ru", "r", stdin);
freopen("out.ru", "w", stdout);
#endif
in(n);
for(int i = 1; i <= n; i ++) hs[i] = in(a[i]);
for(int i = 1; i <= n; i ++) in(q[i].l), in(q[i].r), q[i].id = i, pos[i] = (i - 1) / B +1;
for(int i = 1; R[i - 1] != n; i ++) {
L[i] = (i - 1) * K + 1, R[i] = min(i * K, n);
for(int j = L[i]; j <= R[i]; j ++) bel[j] = i;
}
sort(q + 1, q + 1 + n, [](const query &a, const query &b)
{ return pos[a.r] == pos[b.r] ? pos[a.r] & 1 ? a.l < b.l : a.l > b.l : pos[a.r] < pos[b.r];});
sort(hs + 1, hs + 1 + n);
int len = unique(hs + 1, hs + 1+ n) - hs - 1;
for(int i = 1; i <= n; i ++)
a[i] = lower_bound(hs + 1, hs + len + 1, a[i]) - hs;
for(int i = 1, l = 1, r = 0, MX, ID; i <= n; i ++) {
while(l > q[i].l) -- l, rt[bel[a[l]]].update(a[l]);
while(r < q[i].r) ++ r, rt[bel[a[r]]].update(a[r]);
while(l < q[i].l) rt[bel[a[l]]].reduce(a[l]), ++ l;
while(r > q[i].r) rt[bel[a[r]]].reduce(a[r]), -- r;
MX = 0;
for(int i = 1; i <= bel[n]; i ++) {
if(rt[i].mx[1] > MX) {
MX = rt[i].mx[1];
ID = rt[i].id[1];
}
}
ans[q[i].id] = hs[ID];
}
for(int i = 1; i <= n; i ++) out(ans[i]), en_;
}
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~
Part II:个位数的块长,O(n m^{\frac {2}{3}})
这个倒是听说过呢。
如上所言,一般分块和莫队搭档都是十分好的,但是有时候,你发现:
就是如果你的修改、查询都只会做
同样的,令块长为
一个显然的思路是把块长改成
你可以考虑分两层块,也就是什么块套块,第一层块长
然后就是标题,你发现一般
然后这个码有 Solwek 的帮助(详见),特在此感谢一下。
code
Solwek 写的特别快,我就算了,懒得卡常。
// code by 樓影沫瞬_Hz17 & Solwek
#include <bits/stdc++.h>
#define en_ putchar_unlocked('\n')
#define e_ putchar_unlocked(' ')
using namespace std;
template<typename T> inline T in() {
T n = 0; char p = getchar_unlocked();
while (p < '-') p = getchar_unlocked();
bool f = p == '-' ? p = getchar_unlocked() : 0;
do n = n * 10 + (p ^ 48), p = getchar_unlocked();
while (isdigit(p));
return f ? -n : n;
}
template<typename T> inline T in(T &a) { return a = in<T>(); }
template<typename T> inline void out(T n) {
if(n < 0) putchar_unlocked('-'), n = -n;
if(n > 9) out(n / 10);
putchar_unlocked(n % 10 + '0');
}
const int N = 3e5 + 10, B = 577, B2 = 64, B3 = 8;
using pii = pair<int, int>;
int a[N], n, L1[N], R1[N], bel1[N], L2[N], R2[N], bel2[N];
int hs[N];
struct query {
int l, r, id;
} q[N];
int pos[N];
int ans[N];
uint mx1[N], mx2[N], id1[N], id2[N], cnt[N];
inline void upd(int x,int v){
cnt[x] += v;
int id = bel2[x], idp = bel1[x];
mx2[id] = 0;
for(int i = L2[id]; i <= R2[id]; i ++) {
if(cnt[i] > mx2[id]) {
mx2[id] = cnt[i];
id2[id] = i;
}
}
int idl = (idp - 1) * B3 + 1;
int idr = min(bel2[n], idl + B3 - 1);
mx1[idp] = 0;
for(int i = idl; i <= idr; i ++) {
if(mx2[i] > mx1[idp]){
mx1[idp] = mx2[i];
id1[idp] = id2[i];
}
}
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("in.ru", "r", stdin);
freopen("out.ru", "w", stdout);
#endif
in(n);
for(int i = 1; i <= n; i ++) hs[i] = in(a[i]);
for(int i = 1; i <= n; i ++) in(q[i].l), in(q[i].r), q[i].id = i;
for(int i = 1; i <= n; i ++) pos[i] = (i - 1) / B + 1;
for(int i = 1; R1[i - 1] != n; i ++) {
L1[i] = (i - 1) * B2 + 1, R1[i] = min(i * B2, n);
for(int j = L1[i]; j <= R1[i]; j ++) bel1[j] = i;
}
for(int i = 1; R2[i - 1] != n; i ++) {
L2[i] = (i - 1) * B3 + 1, R2[i] = min(i * B3, n);
for(int j = L2[i]; j <= R2[i]; j ++) bel2[j] = i;
}
sort(q + 1, q + 1 + n, [](query a, query b)
{ return pos[a.l] == pos[b.l] ? pos[a.l] & 1 ? a.r < b.r : a.r > b.r : pos[a.l] < pos[b.l];});
sort(hs + 1, hs + 1 + n);
int len = unique(hs + 1, hs + 1+ n) - hs - 1;
for(int i = 1; i <= n; i ++)
a[i] = lower_bound(hs + 1, hs + len + 1, a[i]) - hs;
for(int i = 1, l = 1, r = 0, mx, id; i <= n; i ++) {
while(l < q[i].l) upd(a[l], -1), l ++;
while(l > q[i].l) l --, upd(a[l], 1);
while(r < q[i].r) r ++, upd(a[r], 1);
while(r > q[i].r) upd(a[r], -1), r --;
mx = 0;
for(int j = 1; j <= bel1[n]; j ++) {
if(mx1[j] > mx) {
mx = mx1[j];
id = id1[j];
}
}
ans[q[i].id] = hs[id];
}
for(int i = 1; i <= n; i ++) out(ans[i]), en_;
}
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~