zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 64 Div.2 E

    单调栈/分治

    单调栈做法有点玄学。。首先用单调栈预处理出每个数左边第一个大于他的数和右边第一个大于他的数,这样可以确定在一个范围内该数数最大数。

    然后从小的范围开始遍历,看看对应的另一个值在不在右边。。

    这样复杂度是O(nlogn)而不是O(n^2), 类似启发式合并的证明,然而我太菜了,不知道怎么证明的。。

    #include <bits/stdc++.h>
    #define INF 0x3f3f3f3f
    #define full(a, b) memset(a, b, sizeof a)
    using namespace std;
    typedef long long ll;
    inline int lowbit(int x){ return x & (-x); }
    inline int read(){
        int X = 0, w = 0; char ch = 0;
        while(!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
        while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
        return w ? -X : X;
    }
    inline int gcd(int a, int b){ return a % b ? gcd(b, a % b) : b; }
    inline int lcm(int a, int b){ return a / gcd(a, b) * b; }
    template<typename T>
    inline T max(T x, T y, T z){ return max(max(x, y), z); }
    template<typename T>
    inline T min(T x, T y, T z){ return min(min(x, y), z); }
    template<typename A, typename B, typename C>
    inline A fpow(A x, B p, C lyd){
        A ans = 1;
        for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
        return ans;
    }
    const int N = 300005;
    int s[N], a[N], pos[N], lg[N], rg[N], tot;
    
    int main(){
    
        int n = read(), ans = 0;
        for(int i = 1; i <= n; i ++){
            a[i] = read(), pos[a[i]] = i;
        }
        a[0] = a[n + 1] = INF;
        for(int i = 0; i <= n; i ++){
            while(tot && a[s[tot]] < a[i]) tot --;
            lg[i] = s[tot], s[++tot] = i;
        }
        tot = 0;
        for(int i = n + 1; i >= 1; i --){
            while(tot && a[s[tot]] < a[i]) tot --;
            rg[i] = s[tot], s[++tot] = i;
        }
        for(int i = 1; i <= n; i ++){
            if(i - lg[i] < rg[i] - i){
                for(int j = lg[i] + 1; j < i; j ++){
                    int tmp = pos[a[i] - a[j]];
                    if(tmp > i && tmp < rg[i]) ans ++;
                }
            }
            else{
                for(int j = i + 1; j < rg[i]; j ++){
                    int tmp = pos[a[i] - a[j]];
                    if(tmp < i && tmp > lg[i]) ans ++;
                }
            }
        }
        cout << ans << endl;
        return 0;
    }
    

    还有一种分治做法,就好理解多了。

    就是在合并区间的时候,统计一下左半部分后缀的最大值和右半部分前缀的最大值,每个位置依次枚举,看看跨越mid部分有没有对应的另一个值,而且跨越了mid部分的最大值也要小于当前的最大值。

    #include <bits/stdc++.h>
    #define INF 0x3f3f3f3f
    #define full(a, b) memset(a, b, sizeof a)
    using namespace std;
    typedef long long ll;
    inline int lowbit(int x){ return x & (-x); }
    inline int read(){
        int X = 0, w = 0; char ch = 0;
        while(!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
        while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
        return w ? -X : X;
    }
    inline int gcd(int a, int b){ return a % b ? gcd(b, a % b) : b; }
    inline int lcm(int a, int b){ return a / gcd(a, b) * b; }
    template<typename T>
    inline T max(T x, T y, T z){ return max(max(x, y), z); }
    template<typename T>
    inline T min(T x, T y, T z){ return min(min(x, y), z); }
    template<typename A, typename B, typename C>
    inline A fpow(A x, B p, C lyd){
        A ans = 1;
        for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
        return ans;
    }
    const int N = 300005;
    int ans, a[N], pos[N], mx[N];
    
    void solve(int l, int r){
        if(r - l + 1 <= 2) return;
        int mid = (l + r) >> 1;
        solve(l, mid), solve(mid + 1, r);
        mx[mid] = a[mid], mx[mid + 1] = a[mid + 1];
        for(int i = mid - 1; i >= l; i --){
            mx[i] = max(mx[i + 1], a[i]);
        }
        for(int i = mid + 2; i <= r; i ++){
            mx[i] = max(mx[i - 1], a[i]);
        }
        for(int i = l; i <= mid; i ++){
            int tmp = pos[mx[i] - a[i]];
            if(tmp > mid && tmp <= r && mx[tmp] < mx[i]) ans ++;
        }
        for(int i = mid + 1; i <= r; i ++){
            int tmp = pos[mx[i] - a[i]];
            if(tmp <= mid && tmp >= l && mx[tmp] < mx[i]) ans ++;
        }
    }
    
    int main(){
    
        int n = read();
        for(int i = 1; i <= n; i ++){
            a[i] = read(), pos[a[i]] = i;
        }
        solve(1, n);
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    PHP 5.3.X 连接MS SQL Server php_mssql.dll
    Elk+redis的配置
    MongoDB增加用户认证: 增加用户、删除用户、修改用户密码、读写权限、只读权限
    在 CentOS7 上安装 MySQL5.7
    CentOS挂载新硬盘
    Linux 启动和关闭自定义命令
    CentOS7中firewall防火墙详解和配置,.xml服务配置详解
    Linux --centos7 开机启动设置
    vmware centos7 静态ip设置
    Linux下安装Nginx详细图解教程(一)
  • 原文地址:https://www.cnblogs.com/onionQAQ/p/10815974.html
Copyright © 2011-2022 走看看