AcWing 241. 楼兰图腾

 


 一道本蒟蒻想不到的题目。

暴力模拟是求出每一个点左边和右边都比他大和小的数目,然后相乘输出。

tip:左边比他小的都求出来之后,那剩下的都是比他大的,只用扫一遍

TLE代码(时间复杂度:O(n^2))

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

long long n,y[200001],lmin[200001],rmin[200001],lmax[200001],rmax[200001],ansMidMax,ansMidMin;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr),cout.tie(nullptr);

    cin>>n;
    for(int i=1;i<=n;++i)
        cin>>y[i];

    for(int i=1;i<=n;++i){
        for(int j=1;j<i;++j){
            lmin[i]+=y[j]<y[i];
        }
        lmax[i]=i-1-lmin[i];
        for(int j=i+1;j<=n;++j){
            rmax[i]+=y[j]>y[i];
        }
        rmin[i]=n-(i+1)+1-rmax[i];
    }

    for(int i=1;i<=n;++i){
        ansMidMax+=lmin[i]*rmin[i];
        ansMidMin+=lmax[i]*rmax[i];
    }
    cout<<ansMidMin<<" "<<ansMidMax;
    return 0;
}

算法核心为下面这串公式(以左边比他小的举例)

一处本蒟蒻想不到的地方出现了,当线性跑过序列y的时候,(假如输入的是k)如果存入不是y[i]=k,存入的是y[k]=true(哈希)的话,那么公式就可以变成

显然右边的式子就是一个前缀和式子,但是他是需要随着遍历动态更新的前缀和。

即线性跑过去只会多一个元素,相当于每次插入一个元素,并更新区间,输出就是查询区间[1,i-1]已经存在的数。

由于左边的数不是比目标数大就是小,所以只需要维护一个比他小或者比他大的,另一种情况用总数减一下即可。

那么选用树状数组来维护这个动态前缀和。

AC代码

#include <bits/stdc++.h>
using namespace std;
const int N=200001;

long long n,c[N],lmin[N],lmax[N],rmin[N],rmax[N],a[N],ansMidDown,ansMidUp;

int lowbit(int i){
    return i&-i;
}
void add(int i,int val){
    while(i<=n){
        c[i]+=val;
        i+=lowbit(i);
    }
}
int sum(int i){
    int res=0;
    while(i>0){
        res+=c[i];
        i-=lowbit(i);
    }
    return res;
}

int main(){
    cin>>n;
    for(int i=1;i<=n;++i){
        cin>>a[i];
        add(a[i],1);
        lmin[i]=sum(a[i]-1);
        lmax[i]=i-1-lmin[i];
    }
    for(int i=1;i<=n;++i)c[i]=0;
    for(int i=n;i>=1;--i){
        add(a[i],1);
        rmax[i]=sum(n)-sum(a[i]);
        rmin[i]=n-i-rmax[i];
    }
    
    for(int i=1;i<=n;++i){
        ansMidUp+=lmin[i]*rmin[i];
        ansMidDown+=lmax[i]*rmax[i];
    }
    cout<<ansMidDown<<" "<<ansMidUp;
    return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值