1. 程式人生 > 實用技巧 >【CF-484E】Sign on Fence && 【luogu-P2839】[國家集訓隊]middle (二分+主席樹的妙用整理)

【CF-484E】Sign on Fence && 【luogu-P2839】[國家集訓隊]middle (二分+主席樹的妙用整理)

【CF-484E】Sign on Fence 題目連結:https://codeforces.ml/contest/484/problem/E

【luogu-P2839】[國家集訓隊]middle 題目連結:https://www.luogu.com.cn/problem/P2839

思路

這兩道題都應用了一個思想:將小於(等於)其值的數的位置和大於其值的位置分開討論。

具體可以在這兩行程式碼實現:

for (int i = 1; i <= n; i++) {
    vec[h[i]].push_back(i);
}
root[0] = tree.build(1, n);
for (int i = 1; i <= Discrete::blen; i++) {
    root[i] = root[i-1];
    for (auto e: vec[i]) {
    root[i] = tree.update(root[i], e, 0, 1, n);
}

【CF-484E】Sign on Fence 具體思路

二分答案,將值大於二分出來的 \(mid\) 的位置置為 \(1\),檢查在區間連續範圍內是否有一段長度為 \(k\)

【CF-484E】Sign on Fence AC程式碼

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 1e5 + 5;

namespace Discrete {      // 祖傳離散化
    int b[MAXN], blen, btol;
    void insert(int x) { b[btol++] = x; }
    void init() {
        sort(b, b + btol);
        blen = unique(b, b + btol) - b;
    }
    int val2id(int x) {
        return lower_bound(b, b + blen, x) - b + 1;
    }
    int id2val(int x) {return b[x-1];}
}
using Discrete::val2id;
using Discrete::id2val;

class HJT {
public:
    struct node {
        int ch[2];
        int pre, suf, len;
        node() {
            ch[0] = ch[1] = pre = suf = len  = 0;
        }
    } T[MAXN*70];
    int tot = 0;

#define lson T[rt].ch[0]
#define rson T[rt].ch[1]

    inline void push_up(int rt, int be, int en) {
        T[rt].pre = T[lson].pre, T[rt].suf = T[rson].suf;
        T[rt].len = max(T[lson].suf + T[rson].pre, max(T[lson].len, T[rson].len));
        int mid = (be + en) >> 1;
        if (T[lson].pre == mid-be+1) T[rt].pre += T[rson].pre;
        if (T[rson].suf == en- (mid+1) + 1) T[rt].suf += T[lson].suf;
    }

    int build(int l, int r) {
        int nrt = ++tot;
        if (l == r) {
            T[nrt].len = T[nrt].pre = T[nrt].suf = 1;
            return nrt;
        }
        int mid = (l + r) >> 1;
        T[nrt].ch[0] = build(l, mid), T[nrt].ch[1] = build(mid + 1, r);
        push_up(nrt, l, r);
        return nrt;
    }

    int update(int rt, int pos, int v, int be, int en) {
        int nrt = ++tot;
        if (be == en) {
            T[nrt].len = T[nrt].pre = T[nrt].suf = v;
            return nrt;
        }
        int mid = (be + en) >> 1;
        if (pos <= mid) {
            T[nrt].ch[0] = update(lson, pos, v, be, mid);
            T[nrt].ch[1] = rson;
        } else {
            T[nrt].ch[0] = lson;
            T[nrt].ch[1] = update(rson, pos, v, mid + 1, en);
        }
        push_up(nrt, be, en);
        return nrt;
    }

    node query_max(int rt, int L, int R, int be, int en) {
        if (L <= be && en <= R) return T[rt];
        int mid = (be + en) >> 1;
        if (R <= mid) return query_max(lson, L, R, be, mid);
        else if (L > mid) return query_max(rson, L, R, mid+1, en);
        else {
            node ans;
            node ansl = query_max(lson, L, R, be, mid);
            node ansr = query_max(rson, L, R, mid+1, en);
            ans.pre = ansl.pre, ans.suf = ansr.suf;
            ans.len = max(ansl.suf + ansr.pre, max(ansl.len, ansr.len));
            if (ansl.pre == mid - be + 1) ans.pre += ansr.pre;
            if (ansr.suf == en - (mid+1) + 1) ans.suf += ansl.suf;
            return ans;
        }
    }
}tree;




int h[MAXN], root[MAXN];
vector<int> vec[MAXN];
int n;
bool check(int mid, int l, int r, int k) {
   // printf("%d\n", tree.query_max(root[mid-1], l, r, 1, n).len);
    if (tree.query_max(root[mid-1], l, r, 1, n).len >= k) return 1;
    else return 0;
}

int main() {

    scanf("%d", &n);
    Discrete::btol = 0;
    for (int i = 1; i <= n; i++) scanf("%d", &h[i]), Discrete::insert(h[i]);
    Discrete::init();
    for (int i = 1; i <= n; i++) h[i] = val2id(h[i]);

    for (int i = 1; i <= n; i++) {
        vec[h[i]].push_back(i);
    }

    root[0] = tree.build(1, n);

    for (int i = 1; i <= Discrete::blen; i++) {
        root[i] = root[i-1];
        for (auto e: vec[i]) {
            root[i] = tree.update(root[i], e, 0, 1, n);
        }
    }

    int m; scanf("%d", &m);
    while (m--) {
        int l, r, k; scanf("%d%d%d", &l, &r, &k);
        int L = 1, R = Discrete::blen;
        while (L < R) {
            int mid = (L+R+1)>>1;
            if (check(mid,l,r, k)) L = mid;
            else R = mid-1;
        }
        printf("%d\n", id2val(L));
    }

}

/*
10
1 2 3 4 5 6 7 8 9 10
1
2 7 5
*/

【luogu-P2839】[國家集訓隊]middle 具體思路

二分列舉中位數,應用到具體的性質為:如果一段數將大於 \(mid\) 的數賦為 \(-1\),小於 \(mid\) 的數賦值為 \(1\),那麼和 \(= 0\) 的話,那麼則可取。

再在查詢的範圍內尋找字首和字尾最大的和,若和 \(\geq 0\),則代表這段序列可以通過縮小來使得最終的和 \(= 0\)

【luogu-P2839】[國家集訓隊]middle AC程式碼

#include <bits/stdc++.h>

#define inf 0x3f3f3f3f
#define pb push_back
using namespace std;
const int MAXN = 2e4 + 5;

namespace Discrete {      // 祖傳離散化
    int b[MAXN << 1], btol, blen;
    void insert(int x) { b[btol++] = x; }
    void init() {
        sort(b, b + btol);
        blen = unique(b, b + btol) - b;
    }
    int val2id(int x) { return lower_bound(b, b + blen, x) - b + 1; }
    int id2val(int x) { return b[x - 1]; }
}
using Discrete::val2id;
using Discrete::id2val;


class HJT {
public:
    struct node {
        int ch[2];
        int pre, suf, sum;
        node() {
            ch[0] = ch[1] = pre = suf = sum = 0;
        }
    } T[MAXN * 70];
    int tot;
#define lson T[rt].ch[0]
#define rson T[rt].ch[1]

    inline void push_up(int rt) {
        T[rt].sum = T[lson].sum + T[rson].sum;
        T[rt].pre = max(T[lson].pre, T[lson].sum + T[rson].pre);
        T[rt].suf = max(T[rson].suf, T[rson].sum + T[lson].suf);
    }

    int build(int l, int r) {
        int nrt = ++tot;
        if (l == r) {
            T[nrt].sum = T[nrt].pre = T[nrt].suf = 1;
            return nrt;
        }
        int mid = (l + r) >> 1;
        T[nrt].ch[0] = build(l, mid), T[nrt].ch[1] = build(mid + 1, r);
        push_up(nrt);
        return nrt;
    }

    int update(int rt, int pos, int v, int be, int en) {
        int nrt = ++tot;
        if (be == en) {
            T[nrt].sum = T[nrt].pre = T[nrt].suf = v;
            return nrt;
        }
        int mid = (be + en) >> 1;
        if (pos <= mid) {
            T[nrt].ch[0] = update(lson, pos, v, be, mid);
            T[nrt].ch[1] = rson;
        } else {
            T[nrt].ch[0] = lson;
            T[nrt].ch[1] = update(rson, pos, v, mid + 1, en);
        }
        push_up(nrt);
        return nrt;
    }

    int query_sum(int rt, int L, int R, int be, int en) {
        if (L <= be && en <= R) return T[rt].sum;
        int mid = (be + en) >> 1;
        int ans = 0;
        if (L <= mid) ans += query_sum(lson, L, R, be, mid);
        if (R > mid) ans += query_sum(rson, L, R, mid + 1, en);
        return ans;
    }

    node query_pre(int rt, int L, int R, int be, int en) {
        if (L <= be && en <= R) return T[rt];
        int mid = (be + en) >> 1;
        if (R <= mid) return query_pre(lson, L, R, be, mid);
        else if (L > mid) return query_pre(rson, L, R, mid + 1, en);
        else {
            node ans;
            node ansl = query_pre(lson, L, R, be, mid);
            node ansr = query_pre(rson, L, R, mid + 1, en);
            ans.sum = ansl.sum + ansr.sum;
            ans.pre = max(ansl.pre, ansl.sum + ansr.pre);
            return ans;

        }
    }

    node query_suf(int rt, int L, int R, int be, int en) {
        if (L <= be && en <= R) return T[rt];
        int mid = (be + en) >> 1;
        if (R <= mid) return query_suf(lson, L, R, be, mid);
        else if (L > mid) return query_suf(rson, L, R, mid + 1, en);
        else {
            node ans;
            node ansl = query_suf(lson, L, R, be, mid);
            node ansr = query_suf(rson, L, R, mid + 1, en);
            ans.sum = ansl.sum + ansr.sum;
            ans.suf = max(ansr.suf, ansr.sum + ansl.suf);
            return ans;
        }
    }
} tree;


int a[MAXN], root[MAXN];
vector<int> vec[MAXN];

int n;

bool check(int mid, const vector<int> &q) {
    int sum = 0;
    if (q[1] + 1 <= q[2] - 1) sum = tree.query_sum(root[mid - 1], q[1] + 1, q[2] - 1, 1, n);
    sum += tree.query_suf(root[mid - 1], q[0], q[1], 1, n).suf;
    sum += tree.query_pre(root[mid - 1], q[2], q[3], 1, n).pre;
    if (sum >= 0) return 1;
    else return 0;
}


int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        Discrete::insert(a[i]);
    }
    Discrete::init();

    for (int i = 1; i <= n; i++) a[i] = val2id(a[i]);

    for (int i = 1; i <= n; i++) {
        vec[a[i]].pb(i);
    }

    root[0] = tree.build(1, n);
    for (int i = 1; i <= Discrete::blen; i++) {
        root[i] = root[i - 1];
        for (auto e: vec[i]) {
            root[i] = tree.update(root[i], e, -1, 1, n);
        }
    }
    int m;
    scanf("%d", &m);
    int lastans = 0;
    while (m--) {
        vector<int> q(4);
        scanf("%d%d%d%d", &q[0], &q[1], &q[2], &q[3]);
        for (int i = 0; i < 4; i++) q[i] = (q[i] + lastans) % n + 1;
        sort(q.begin(), q.end());
        int L = 1, R = Discrete::blen;
        while (L < R) {
            int mid = (L + R + 1) >> 1;
            if (check(mid, q)) L = mid;
            else R = mid - 1;
        }
        lastans = id2val(L);
        printf("%d\n", lastans);

    }
}

/*
5
2 4 1 5 3
1
3 1 0 2
*/