Y
K
N
U
F

题解:P6349 [PA 2011] Kangaroos

跳回 luogu

提供一个比较无脑的 \(O(mn^\frac34)\) 做法。话说竟然没卡常,本来都准备好了的……

正文(为了醋包的饺子)

首先发现题目的 \(l,r\) 的范围很大,但是可以通过离散化拍到 \([1,2n]\) 上面,所以就拍上去,一会有用。

发现这样一来 \(A,B\) 的范围就很小了,可以考虑用维护区间的算法做。

所以转化题意。

维护一个 \(cnt_i\) 表示 \(a_i\) 的区间是否和 \([A,B]\) 有交,所求就是 \(cnt_i\) 的最长连续 \(1\) 串的长度。

关于维护 \(cnt\),可以用莫队维护,具体的,考虑不去重离散化数组,给每一个位置标记它是哪一个 \(a_i\)\(l_i\)\(r_i\)
如果莫队的 \(l\) 指针遇见或离开了新的 \(a_i\)\(r_i\) 就减少或增加 \(cnt_i\),另一边同理。

这样就有了一个完整的 \(cnt\) 序列,那么考虑维护 \(1\) 串的长度,一个显然的想法是线段树,平衡完可以做到一个略小于 \(O(m\sqrt n\log\sqrt n)\) 的复杂度(不可解),但是想做到这个就要分块每块单独维护一颗线段树,写起来很难受,所以考虑直接分块。

思考线段树维护要维护什么,某区间左和右端最长 \(1\) 串和区间内最长 \(1\) 串对吧。分块也维护这个。

令块长为 \(K\),发现并不好做什么 \(O(1)\) 修改 \(O(\frac nK)\) 查询的东西,好像只能 \(O(K)\) 修改 \(O(\frac nK)\) 查询,因为有着 \(m\sqrt n\) 次修改和 \(m\) 次查询,这时 \(K\) 如果取 \(\sqrt n\) 显然很暴力(但是使用下述技巧是能过的)。容易发现 \(K\) 取到 \(n^{\frac 14}\) 有最优复杂度 \(O(mn^{\frac 34})\)。所以就这么取。

卡常(醋)

本来以为这个复杂度很烂所以要卡常。结果最慢点只有 \(1.4s\)。但是我感觉不讲这个那这篇题解太没营养了,更主要的是,这篇题解根本就是饺子,而醋就是这个。

跳过诸如莫队奇偶排序,快读等老生常谈,直接讲我想讲的!

发现所谓修改就是重构整个块,重算整个块的贡献,那么可以考虑懒重构。

其实也算是老生常谈什么的但是很有效所以想讲的,说白了就是修改了哪个块就给那个块打个标记,不立刻重构,计算贡献的时候发现这个块有标记就重构一下这个块,比较简单但是效果拔群,具体讲甚至可以让 \(O(mn)\) 通过此题。

代码(码风可能不太优良)

// code by 樓影沫瞬_Hz17
#include <bits/stdc++.h>

using std::max; using std::min;

constexpr int N = 5e4 + 10, M = 2e5 + 10;

static int n, m;

struct Point { 
    int p, id; bool isl; // 分别是 "位置" "所属ai的i" "是不是l" 的意思
    const bool operator < (const Point&P) const { return p < P.p; }    
    const bool operator > (const Point&P) const { return p > P.p; }    
} o[N * 2];

constexpr int B = 430, BN = N / B + 10;

int pos[N * 2]; // 莫队的所属块

struct Que { int id, l, r; } Q[M];

int ans[M];

constexpr int K = 50, KN = N / K + 10;

int SL[KN], SR[KN], MX[KN], L[KN], R[KN]; // 分别是 "左端/右端连续1" "块内连续1" "块左/右端点" 
int cnt[N], bel[N]; // 块维护的数组和所在块
bool tag[KN]; // 重构标记

inline void rbuild(int p) { // 重构整个块
    int sl = 0, sr = 0, mx = 0;
    for(int i(L[p]); i <= R[p] and cnt[i]; i ++, sl ++);
    for(int i(R[p]); i >= L[p] and cnt[i]; i --, sr ++);
    for(int i(L[p] + sl), ll(0); i <= R[p] - sr; i ++) ll = cnt[i] ? ll + 1 : 0, mx = max(mx, ll);
    mx = max(sl, mx); mx = max(sr, mx);
    SL[p] = sl, SR[p] = sr, MX[p] = mx;
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("in.ru", "r", stdin);
        freopen("out.ru", "w", stdout);
    #endif
    using std::cin; using std::cout;
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    // 提醒:下面很丑,是故意的,我写着也难受,以后不这么写了
    cin >> n >> m;
    for(int i(1); i <= n; cin >> o[i].p >> o[i + n].p, i ++) o[i + n].id = o[i].id = i, o[i].isl = 1;
    
    for(int i(1), j(0); R[i - 1] != n; i ++) 
        for(j = L[i] = (i - 1) * K + 1, R[i] = min(n, i * K); j <= R[i]; bel[j ++] = i);
    for(int i(1); i <= n + n; pos[i ++] = i / B);
    
    std::sort(&o[1], &o[n + n] + 1);
    auto clac = [](Que&q) { // 因为不能去重所以离散化比较有意思
        q.l = std::lower_bound(&o[1], &o[n + n] + 1, (Point){q.l}) - o;
        q.r = std::upper_bound(&o[1], &o[n + n] + 1, (Point){q.r}) - o - 1;
        Q[q.id] = q;
    }; 
    for(Que q({0, 0, 0}); q.id <= m; cin >> q.l >> q.r, q.id ++) clac(q);
    std::sort(&Q[1], &Q[m] + 1, [](const Que&a, const Que&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]; });
    
    int l = 1, r = 0;
    for(Que*it = &Q[1], q = Q[1]; it != &Q[m + 1]; it ++, q = *it) {
        while(l < q.l) if(!o[l ++].isl) cnt[o[l - 1].id] --, tag[bel[o[l - 1].id]] = 1;
        while(l > q.l) if(!o[-- l].isl) cnt[o[l].id] ++, tag[bel[o[l].id]] = 1;
        while(r < q.r) if(o[++ r].isl) cnt[o[r].id] ++, tag[bel[o[r].id]] = 1;
        while(r > q.r) if(o[r --].isl) cnt[o[r + 1].id] --, tag[bel[o[r + 1].id]] = 1;
        int &as = ans[q.id]; // 和上文对应,只打 tag 不乱动
        for(int i(1), las(0); i <= bel[n]; i ++) { // 求 ans 就是维护一个跨块的连续和就行了。
            if(tag[i]) rbuild(i), tag[i] = 0;
            if(MX[i] == (R[i] - L[i] + 1)) las += MX[i];
            else las += SL[i], as = max(as, max(MX[i], las)), las = SR[i];
            as = max(as, las);
        }
    }

    for(int i(1); i <= m; i ++) cout << ans[i] << '\n';
}
posted @ 2025-11-10 21:54  樓影沫瞬_Hz17  阅读(15)  评论(0)    收藏  举报