• P4755 Beautiful Pair (分治 + 主席树)


    题意:1e5的数组 计算有多少对 ai * aj <= max(ai ai+1...aj-1 aj)

    题解:在处理这种涉及到区间极值的题时 好像是个套路分治 从级值中间分成两个区间

       从区间短的那边暴力枚举算贡献 然后再分治下去

       可以估计复杂度 一个点最多枚举n/2次 两个点最多枚举n/4次 4个点最多枚举n/8次...

       枚举加起来的复杂度是nlogn

       假设枚举了ai作为一个区间端点 问题就转化为统计极值另一边的区间找 <= zd / ai的个数

       我是用主席树 然后写了个类似整体二分求的 看别人题解直接用的树状数组离线做显然复杂度更优秀

       总共复杂度是nlognlogn

    #include <stdio.h>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int MAXN = 1e5 + 5;
    
    ll ans;
    int n, cnt, len;
    int pre[MAXN];
    int a[MAXN], b[MAXN], t[MAXN];
    int zd[MAXN][25];
    int ls[MAXN << 5], rs[MAXN << 5], sum[MAXN << 5];
    
    int build(int l, int r) {
        int rt = ++cnt;
        int mid = l + r >> 1;
        sum[rt] = 0;
        if(l < r) {
            ls[rt] = build(l, mid);
            rs[rt] = build(mid + 1, r);
        }
        return rt;
    }
    
    int add(int o, int l, int r, int k) {
        int rt = ++cnt;
        int mid = l + r >> 1;
        ls[rt] = ls[o]; rs[rt] = rs[o]; sum[rt] = sum[o] + 1;
    
        if(l < r)
            if(k <= mid) ls[rt] = add(ls[o], l, mid, k);
            else rs[rt] = add(rs[o], mid + 1, r, k);
        return rt;
    }
    
    int query(int ql, int qr, int l, int r, int x) {
        if(l == r) return b[l] <= x ? sum[qr] - sum[ql] : 0;
    
        int mid = l + r >> 1;
        if(x <= b[mid]) return query(ls[ql], ls[qr], l, mid, x);
        else return sum[ls[qr]] - sum[ls[ql]] + query(rs[ql], rs[qr], mid + 1, r, x);
    }
    
    void solve(int l, int r) {
        if(l >= r) return;
        int lg = log2(r - l + 1);
        int index;
        if(a[zd[l][lg]] > a[zd[r - (1 << lg) + 1][lg]]) index = zd[l][lg];
        else index = zd[r - (1 << lg) + 1][lg];
    
        if(index - l < r - index) {
            for(int i = l; i < index; i++) {
                //if(a[i] == 1) ans++;
                int now = a[index] / a[i];
                ans += 1LL * query(t[index], t[r], 1, len, now);
            }
        } else {
            for(int i = index + 1; i <= r; i++) {
                //if(a[i] == 1) ans++;
                int now = a[index] /a[i];
                ans += 1LL * query(t[l - 1], t[index - 1], 1, len, now);
            }
        }
        if(r - l + 1 >= 2) ans += 1LL * (pre[r] - pre[index] + pre[index - 1] - pre[l - 1]); 
        solve(l, index - 1);
        solve(index + 1, r);
    }
    
    int main() {
        cnt = 0;
        ans = 0;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];
        for(int i = 1; i <= n; i++) zd[i][0] = i;
        for(int i = 1; i <= n; i++) {
            pre[i] = pre[i - 1];
            if(a[i] == 1) pre[i]++;
        }
    
        for(int j = 1; j <= 20; j++)
            for(int i = 1; i + (1 << j) - 1 <= n; i++)
                if(a[zd[i][j - 1]] > a[zd[i + (1 << j - 1)][j - 1]]) zd[i][j] = zd[i][j - 1];
                else zd[i][j] = zd[i + (1 << j - 1)][j - 1];
    
        sort(b + 1, b + 1 + n);
        len = unique(b + 1, b + 1 + n) - b - 1;
        t[0] = build(1, len);
        for(int i = 1; i <= n; i++) {
            int tt = lower_bound(b + 1, b + 1 + len, a[i]) - b;
            t[i] = add(t[i - 1], 1, len, tt);
        }
    
        solve(1, n);
        for(int i = 1; i <= n; i++)
            if(a[i] == 1) ans++;
        printf("%lld
    ", ans);
        return 0;
    }
    View Code
  • 相关阅读:
    String类的intern()方法,随常量池发生的变化
    JDK8的JVM内存结构,元空间替代永久代成为方法区及常量池的变化
    wait()、notify()方法原理,以及使用注意事项--丢失唤醒、虚假唤醒
    消费者、生产者Java代码示例,wait-notify实现
    volatile、Synchronized实现变量可见性的原理,volatile使用注意事项
    CAS及其ABA问题
    JUC包Lock机制的支持--AQS
    JUC包实现的同步机制,原理以及简单用法总结
    Synchronized机制下偏向锁、轻量级锁、重量级锁的适用场景
    临时表循环插入
  • 原文地址:https://www.cnblogs.com/lwqq3/p/12295820.html
Copyright © 2020-2023  润新知