zoukankan      html  css  js  c++  java
  • Luogu 4755 Beautiful Pair

    分治 + 主席树。

    设$solve(l, r)$表示当前处理到$[l, r]$区间的情况,我们可以找到$[l, r]$中最大的一个数的位置$mid$,然后扫一半区间计算一下这个区间的答案。

    注意,这时候左半边是$[l, mid]$,而右区间是$[mid, r]$,我们在这个区间处理的时候要算完所有$mid$的情况,然后我们每一次分治的时候去处理$solve(l, mid - 1)$和$solve(mid + 1, r)$,要不然当$mid$是端点的时候就会无限递归下去。

    问题转化快速算出一个区间内$leq$一个数的数,只要一棵主席树就可以解决了,区间最大值可以用$ST$表维护出来。

    我们每一次选取一个比较短的区间去枚举然后算另一个区间的答案,这样子每一次计算区间的长度至少减少一半,这样子可以保证时间复杂度。

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

    Code:

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N = 1e5 + 5;
    const int Lg = 20;
    const ll inf = 1LL << 60; 
    
    int n, tot = 0;
    ll ans = 0LL, a[N], num[N];
    
    template <typename T>
    inline void read(T &X) {
        X = 0; char ch = 0; T 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;
    }
    
    template <typename T>
    inline void chkMax(T &x, T y) {
        if(y > x) x = y;
    }
    
    namespace ST {
        int st[N][Lg], len[N];
        
        inline int bet(int x, int y) {
            return a[x] > a[y] ? x : y;
        }
        
        inline void prework() {
            for(int j = 1; j <= 18; j++)
                for(int i = 1; i + (1 << j) - 1 <= n; i++)
                    st[i][j] = bet(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
        }
        
        inline int qMax(int x, int y) {
            int k = len[y - x + 1];
            return bet(st[x][k], st[y - (1 << k) + 1][k]);
        }
        
    } using namespace ST;
    
    namespace SegT {
        struct Node {
            int lc, rc;
            ll sum;
        } s[N * 40];
        
        int root[N], nodeCnt = 0;
        
        #define lc(p) s[p].lc
        #define rc(p) s[p].rc
        #define sum(p) s[p].sum
        #define mid ((l + r) >> 1)
        
        void ins(int &p, int l, int r, int x, int pre) {
            s[p = ++nodeCnt] = s[pre];
            ++sum(p);
            if(l == r) return;
            
            if(x <= mid) ins(lc(p), l, mid, x, lc(pre));
            else ins(rc(p), mid + 1, r, x, rc(pre));
        }
        
        ll query(int r1, int r2, int l, int r, int x, int y) {
            if(x > y) return 0LL;
            if(x <= l && y >= r) return sum(r2) - sum(r1);
             
            ll res = 0LL;
            if(x <= mid) res += query(lc(r1), lc(r2), l, mid, x, y);
            if(y > mid) res += query(rc(r1), rc(r2), mid + 1, r, x, y);
            return res;
        }
        
        #undef mid
        
    } using namespace SegT;
    
    void solve(int l, int r) {
        if(l > r) return;
        
        int mid = qMax(l, r);
        if(mid - l < r - mid) {
            for(int i = l; i <= mid; i++) {
                int pos = upper_bound(num + 1, num + 1 + tot, (ll) (num[a[mid]] / num[a[i]])) - num - 1;
                ans += query(root[mid - 1], root[r], 1, tot, 1, pos);
            }    
        } else {
            for(int i = mid; i <= r; i++) {
                int pos = upper_bound(num + 1, num + 1 + tot, (ll) (num[a[mid]] / num[a[i]])) - num - 1;
                ans += query(root[l - 1], root[mid], 1, tot, 1, pos);
            }
        }
        
        solve(l, mid - 1), solve(mid + 1, r);
    }
    
    int main() {
        read(n);
        for(int i = 1; i <= n; i++) {
            read(a[i]);
            len[i] = log2(i), st[i][0] = i;
            num[++tot] = a[i];
        }
        prework();
        
        num[++tot] = inf;
        sort(num + 1, num + 1 + tot);
        tot = unique(num + 1, num + tot + 1) - num - 1;
        
        for(int i = 1; i <= n; i++) {
            a[i] = lower_bound(num + 1, num + 1 + tot, a[i]) - num;
            ins(root[i], 1, tot, a[i], root[i - 1]);
        }
        
    /*    for(int i = 1; i <= n; i++) 
            printf("%lld ", a[i]);
        printf("
    ");   */
        
        solve(1, n);
        
        printf("%lld
    ", ans);
        return 0;
    }
    View Code
  • 相关阅读:
    PAT (Advanced Level) Practice 1071 Speech Patterns (25分)
    PAT (Advanced Level) Practice 1070 Mooncake (25分)
    PAT (Advanced Level) Practice 1069 The Black Hole of Numbers (20分)
    PAT (Advanced Level) Practice 1074 Reversing Linked List (25分)
    PAT (Advanced Level) Practice 1073 Scientific Notation (20分)
    第一次冲刺个人总结01
    构建之法阅读笔记01
    人月神话阅读笔记01
    四则运算2
    学习进度条(软件工程概论1-8周)
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9905867.html
Copyright © 2011-2022 走看看