第二大数字和-数据结构

代码源OJ #846. 第二大数字和
在这里插入图片描述
本文为 O ( n ) O(n) O(n)解法;

思路

我们考虑关于每一个数,以该数为第二大数字和的区间数在排列中该数左右两侧的第一个和第二个大于该数的数的位置有关;

如果记该数位置为 p ,左侧第一个大于该数的位置为 pl1 ,左侧第二个大于该数的位置为 pl2(右侧同理 pr1,pr2 ),( p r 2 > p r 1 > p > p l 1 > p l 2 pr2>pr1>p>pl1>pl2 pr2>pr1>p>pl1>pl2)则以该点为第二大数字的区间有 ( p l 2 − p l 1 ) ( p r 1 − p ) + ( p r 2 − p r 1 ) ( p − p l 1 ) (pl_2-pl_1)(pr_1-p)+(pr2-pr1)(p-pl1) (pl2pl1)(pr1p)+(pr2pr1)(ppl1)

在操作过程中,我们选择使用类似链表的数据结构,首先假定所有数的左右两侧数都大于它自身,即 p l 1 = p − 1 , p r 1 = p + 1 pl_1=p-1,pr_1=p+1 pl1=p1,pr1=p+1 ,从小到大处理每一个数,处理后将其左右的相邻数中的 pl1 和 pr1 更新,并抛弃这个数(类似链表中删除元素)。在这个过程中我们就可以保证每个数的 pl1 与 pr1 在使用时一定是正确的;

在这个过程中,px2 可以通过嵌套表示出, [ p x 1 ] x 1 [px_1]x_1 [px1]x1在被使用时并不是数px1 的x方向第一个大于其的数,而是数p 的x方向第一个大于其的数(因为数据还没有处理到数px1,只处理了小于数p 的部分)。因此我们只需要妥善维护每个点 p 的 pl1 与 pr1 即可;

在下面的代码实现中,存储的并不是 px1,而是 ∣ p − p x 1 ∣ |p-px_1| ppx1 ,实际上没有区别;

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct ln{
    int l,r;
    ll tl,tr;
    int num;
}node[100005];
int pos[100005];
int main()
{
    int n,tmp;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&tmp);
        pos[tmp]=i;
        node[i].l=i-1;
        node[i].r=i+1;
        node[i].tl=node[i].tr=1;
        node[i].num=tmp;
    }
    // node[1].tl=node[n].tr=0;
    ll ans=0;
    for(ll i=1;i<=n-1;i++)
    {
        int mk=pos[i];
        // printf("*%d %d\n",i,(node[mk].tl*node[node[mk].r].tr+node[mk].tr*node[node[mk].l].tl));
        ans+=i*(node[mk].tl*node[node[mk].r].tr+node[mk].tr*node[node[mk].l].tl);
        node[node[mk].l].tr+=node[mk].tr;//维护左右邻
        node[node[mk].r].tl+=node[mk].tl;
        node[node[mk].l].r=node[mk].r;//删除元素
        node[node[mk].r].l=node[mk].l;
    }
    cout<<ans;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值