• Luogu 2839 [国家集训队]middle


    感觉这题挺好的。

    首先对于中位数最大有一个很经典的处理方法就是二分,每次二分一个数组中的下标$mid$,然后我们把$mid$代回到原来的数组中检查,如果一个数$a_{i} geq mid$,那么就把$s_{i}$记为$1$,否则把$s_{i}$记为$-1$,然后对$s_{i}$跑一遍前缀和,观察是否有一个区间的和不小于$0$。

    读清楚题意之后发现在这题中,如果要对一个长度为偶数(记为$n$)的序列求中位数,那么答案为排好序的数组中下标为$n / 2 + 1$的元素。(下标从$1$开始),不同的中位数$s_{i}$的表示方法可能有少许不同,要具体题目具体yy。

    证明应当也很简单,$+1$代表一个比当前的$mid$大的数,而$-1$表示一个比当前的$a_{mid}$小的数,那么当$a_{mid}$是中位数的条件在一个区间成立的时候,这个区间的$s_{i}$和应当恰好等于$0$。而当得到了一个区间和大于$0$的区间的时候,我们可以通过扔掉几个$1$使它变成$0$,相当于之前的条件成立。

    但是这样还是太慢了。

    对于本题来说,每次二分得到了一个$mid$,我们检验的时候得到的答案就一定是(区间是$a, b, c, d$)$sum(b + 1, c - 1) + lmax(c, d) + rmax(a, b)$。

    其中$lmax$代表一定要选左端点的最大子段和,而$rmax$代表一定要选右端点的最大子段和。

    显然可以用线段树来维护。

    一个元素开一颗线段树是不可能的……

    我们发现在排好序中的数组里面两个下标相差$1$的元素的值其实只有$1$个不同,这就是较小的元素在原数组中的下标,那么大多数结点的值可以继承过来。

    于是可以可持久化了,变成了一个主席树。

    时间复杂度$O(nlog^{2}n)$。

    Code:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 2e4 + 5;
    const int inf = 1 << 30;
    
    int n, qn, a[N];
    
    struct Innum {
        int val, id;
    } b[N];
    
    bool cmp(const Innum &x, const Innum &y) {
        if(x.val != y.val) return x.val < y.val;
        else return x.id < y.id;
    }   
    
    /*bool cmp(const Innum &x, const Innum &y) {
        return x.val < y.val;
    }   */
    
    inline void read(int &X) {
        X = 0; char ch = 0; int op = 1;
        for(; ch > '9' || ch < '0'; ch = getchar())
            if(ch == '-') op = -1;
        for(; ch >= '0' && ch <= '9'; ch = getchar())
            X = (X << 3) + (X << 1) + ch - 48;
        X *= op;
    }
    
    inline int max(int x, int y) {
        return x > y ? x : y;
    }
    
    inline void chkMax(int &x, int y) {
        if(y > x) x = y;
    }
    
    namespace PSegT {
        struct Node {
            int lc, rc, sum, lmax, rmax;
            
            inline void init() {
                lc = rc = sum = 0;
                lmax = rmax = -inf;
            }
            
        } s[N * 20];
        
        int root[N], nodeCnt = 0;
        
        #define lc(p) s[p].lc
        #define rc(p) s[p].rc
        #define sum(p) s[p].sum
        #define lmax(p) s[p].lmax
        #define rmax(p) s[p].rmax
        #define mid ((l + r) >> 1)
        
        inline void up(int p) {
            if(!p) return;
            sum(p) = sum(lc(p)) + sum(rc(p));
    //        lmax(p) = rmax(p) = -inf;
            lmax(p) = max(lmax(lc(p)), lmax(rc(p)) + sum(lc(p)));
            rmax(p) = max(rmax(rc(p)), rmax(lc(p)) + sum(rc(p)));
        }
    
        void build(int &p, int l, int r) {
            p = ++nodeCnt;
            if(l == r) {
                sum(p) = lmax(p) = rmax(p) = 1;
                return;
            }
            
            build(lc(p), l, mid);
            build(rc(p), mid + 1, r);
            up(p);
        }
        
        void modify(int &p, int l, int r, int x, int v, int pre) {
            s[p = ++nodeCnt] = s[pre];
            if(l == r) {
                sum(p) = lmax(p) = rmax(p) = v;
                return;
            }
            
            if(x <= mid) modify(lc(p), l, mid, x, v, lc(pre));
            else modify(rc(p), mid + 1, r, x, v, rc(pre));
            up(p);
        }
        
        Node query(int p, int l, int r, int x, int y) {
            if(x <= l && y >= r) return s[p];
            
            Node res, ln, rn; 
            res.init(), ln.init(), rn.init();
            
            if(x <= mid) ln = query(lc(p), l, mid, x, y);
            if(y > mid) rn = query(rc(p), mid + 1, r, x, y);
            
            res.sum = ln.sum + rn.sum;
            res.lmax = max(ln.lmax, ln.sum + rn.lmax);
            res.rmax = max(rn.rmax, rn.sum + ln.rmax);
            
            return res;
        }
        
        #undef lc 
        #undef rc 
        #undef sum 
        #undef lmax 
        #undef rmax 
        #undef mid 
        
    } using namespace PSegT;
    
    inline bool chk(int mid, int l1, int r1, int l2, int r2) {
        Node now; int res = 0;
        
        if(r1 + 1 <= l2 - 1) {
            now.init();
            now = query(root[mid], 1, n, r1 + 1, l2 - 1);
            res += now.sum; 
        }
        
        now.init();
        now = query(root[mid], 1, n, l1, r1);
        res += now.rmax;
        
        now.init();
        now = query(root[mid], 1, n, l2, r2);
        res += now.lmax;
        
        return res >= 0;
    }
    
    inline int solve(int l1, int r1, int l2, int r2) {
        int ln = 1, rn = n, mid, res;
        for(; ln <= rn; ) {
            mid = (ln + rn) / 2;
            if(chk(mid, l1, r1, l2, r2)) res = mid, ln = mid + 1;
            else rn = mid - 1;
        }
        return b[res].val;
    }
    
    int main() {
        read(n);
        for(int i = 1; i <= n; i++) {
            read(a[i]);
            b[i].val = a[i], b[i].id = i;
        }
        
        s[0].lmax = s[0].rmax = -inf;
        build(root[1], 1, n);
        sort(b + 1, b + 1 + n, cmp);
        for(int i = 2; i <= n; i++)
            modify(root[i], 1, n, b[i - 1].id, -1, root[i - 1]);
        
        read(qn);
        for(int ans = 0, q[4]; qn--; ) {
            for(int i = 0; i < 4; i++) {
                read(q[i]);
                q[i] = (q[i] + ans) % n + 1;
            }
            sort(q, q + 4);
            ans = solve(q[0], q[1], q[2], q[3]);
            printf("%d
    ", ans);
        }    
        
        return 0;
    }
    View Code
  • 相关阅读:
    第一讲 递归与循环3
    第一讲 递归与循环2
    第一讲 递归与循环1
    [转]批处理
    VBA运算符(九)
    VBA常量(八)
    VBA变量(七)
    VBA输入框(InputBox)(六)
    VBA消息框(MsgBox)(五)
    VBA宏注释(四)
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9646040.html
Copyright © 2020-2023  润新知