【CodeForces - 1156E】Special Segments of Permutation(rmq+递归)

You are given a permutation p of n integers 1, 2, …, n (a permutation is an array where each element from 1 to n occurs exactly once).

Let’s call some subsegment p[l,r] of this permutation special if pl+pr=maxi=lrpi. Please calculate the number of special subsegments.

Input
The first line contains one integer n ( 3 ≤ n ≤ 2 ⋅ 1 0 5 ) . n (3≤n≤2⋅10^5). n(3n2105).

The second line contains n integers p1, p2, …, pn (1≤pi≤n). All these integers are pairwise distinct.

Output
Print the number of special subsegments of the given permutation.

Examples
Input

5
3 4 1 5 2
Output
2
Input
3
1 3 2
Output
1

思路:这题其实我觉得也挺简单的,暴力一点的思路就我们先看整个区间的最大值的位置,然后枚举在他的右边的元素,然后减一下就得到了左边的元素的大小,然后看他的位置是否在他的左边。并不是每次都枚举右边,看那边元素个数小就枚举哪边,那么这样的操作就可以优化到 O ( n l o g n ) O(nlogn) O(nlogn)次。
那么得到区间最大值的位置就显然是rmq了,那么元素在那个位置,我们用一个数组存一下就行,就可以 O ( 1 ) O(1) O(1)了 ,总复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#define INF 0x7f7f7f7f
#define maxx 200005
#define mod 1000000007
using namespace std;
typedef long long ll;
int a[maxx];
int b[maxx];
int n;
int f[maxx][20];
void init()
{
    int N=ceil(log2(n));
    for(int i=1;i<=n;i++)f[i][0]=i;
    for(int i=1;i<=N;i++)
    {
        for(int j=1;j<=n;j++)
        {
            f[j][i]=f[j][i-1];
            if(j+(1<<(i-1))<=n&&a[f[j][i]]<a[f[j+(1<<(i-1))][i-1]])
                f[j][i]=f[j+(1<<(i-1))][i-1];
        }
    }
}
int query(int l,int r)
{
    int t=log2(r-l+1);
    if(a[f[l][t]]>a[f[r-(1<<t)+1][t]])return f[l][t];
    else return f[r-(1<<t)+1][t];
}
int ans=0;
void solve(int l,int r)
{
    if(r-l+1<3)return;
    int mid=query(l,r);
    int left=mid-l;
    int right=r-mid;
    if(left>right)
    {
        for(int i=mid+1;i<=r;i++)
        {
            int now=a[mid]-a[i];
            if(l<=b[now]&&b[now]<mid)ans++;
        }
    }
    else
    {
        for(int i=l;i<mid;i++)
        {
            int now=a[mid]-a[i];
            if(mid<b[now]&&b[now]<=r)ans++;
        }
    }
    solve(l,mid-1);
    solve(mid+1,r);
}
int main()
{

    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",a+i);
        b[a[i]]=i;
    }
    init();
    solve(1,n);
    cout<<ans<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值