Description

给定一个长度为 \(n\) 的排列 \(p\),求有多少区间 \([l,r]\) 满足 \(p[l]+p[r]=max\{p[i]\}\),其中 \(l \le i \le r\)

Solution

单调栈预处理出每个元素的控制区间(以它为最大值的区间)

枚举最大值位置 \(i\),于是 \(l \in [l[i],i], r \in [i,r[i]]\)

考察两个区间的长度,在小的那个中枚举,则只需要检查差是否在大区间中出现

用排列的逆来检查,值 \(x\)\(p_l,...,p_r\) 中出现,即 \(l \le I_x \le r\),其中 \(I\)\(p\) 的逆

显然每个元素被枚举次数的上界为 \(O(\log n)\),于是时间复杂度为 \(O(n \log n)\)

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 200005;
const int dbg = 1;

int p[N],n,inv[N],l[N],r[N];
stack<int> s;

bool check(int x,int l,int r)
{
    if(x<0 || x>n) return 0;
    return l<=inv[x] && inv[x]<=r;
}

signed main()
{
    ios::sync_with_stdio(false);

    cin>>n;
    for(int i=1;i<=n;i++) cin>>p[i], inv[p[i]]=i;

    for(int i=1;i<=n;i++)
    {
        while(s.size() && p[s.top()]<p[i]) s.pop();
        if(s.size()) l[i]=s.top()+1;
        else l[i]=1;
        s.push(i);
    }

    while(s.size()) s.pop();
    for(int i=n;i>=1;--i)
    {
        while(s.size() && p[s.top()]<p[i]) s.pop();
        if(s.size()) r[i]=s.top()-1;
        else r[i]=n;
        s.push(i);
    }

    if(dbg)
    {
        cout<<"monostack output: "<<endl;
        for(int i=1;i<=n;i++) cout<<l[i]<<" ";
        cout<<endl;
        for(int i=1;i<=n;i++) cout<<r[i]<<" ";
        cout<<endl<<endl;
    }

    int ans=0;

    for(int i=1;i<=n;i++)
    {
        int pl,pr,ql,qr;
        if(i-l[i] < r[i]-i)
        {
            pl=l[i];
            pr=i;
            ql=i;
            qr=r[i];
        }
        else
        {
            pl=i;
            pr=r[i];
            ql=l[i];
            qr=i;
        }

        if(dbg)
        {
            cout<<"round "<<i<<": "<<pl<<","<<pr<<" "<<ql<<","<<qr<<endl;
        }

        int tans=0;
        for(int j=pl;j<=pr;j++)
        {
            if(check(p[i]-p[j],ql,qr))
            {
                ++tans;
            }
        }

        ans+=tans;
        if(dbg) cout<<" + "<<tans<<" = "<<ans<<endl;
    }
    cout<<ans<<endl;
}

相关文章: