[HackerRank 101 Hack 42]Array Pairs

该博客介绍了如何解决HackerRank上的一个问题,即计算满足特定条件的数组对数量。通过建立笛卡尔树,并应用启发式合并的思想,将枚举复杂度优化到O(nlog2n),同时提供了使用树状数组进行查询的方法,总时间复杂度为O(nlog2^2n)。文章特别强调了处理特殊情况的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给定一个n个数的数组a,计算出有多少对(i,j)满足i<jai×ajmax(ai,ai+1,...,aj1,aj)

1n5×105,1ai109


题目分析

这题是一个很优美的暴力。
为了方便求出区间最大值,我们先建出笛卡尔树。
考虑枚举笛卡尔树上每一个点,显然可以得到它作为最大值的区间。
如果我们枚举这个区间的左半部分的一个数,结合当前最大值,我们就知道右半区间的合法的数的取值范围。也就是我们只需要查询右半区间有多少个数小于等于某数即可。
但是这样显然是O(n2)的,但是不虚,使用类似启发式合并的想法,如果右边比左边长度小,我们就枚举右边!这样每次枚举长度不会超过整个区间的一半!也就是说枚举的复杂度是O(nlog2n)的!
至于查询,你可以选择打一个主席树在线查。当然也可以先把所有区间询问变为两个前缀询问相减,然后先存下来,遍历完笛卡尔树之后再统一使用树状数组计算。
时间复杂度O(nlog22n)
注意特判当前最大值和别的数组合造成的贡献。


代码实现

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>

using namespace std;

typedef long long LL;

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

const int N=500050;
const int LGN=20;
const int Q=N*LGN<<1;

struct query
{
    int x,a,sign;
}qs[Q];

bool operator<(query p,query q){return p.x<q.x;}

struct D
{
    int key,id;
}srt[N];

bool operator<(D x,D y){return x.key<y.key;}

int A[N],fa[N],son[N][2],stack[N],lab[N],num[N];
int n,q,top,root,idx;
LL ans;

inline int lowbit(int x){return x&-x;}

struct Fenwick_tree
{
    int num[N];

    void modify(int x,int delta){for (;x<=idx;x+=lowbit(x)) num[x]+=delta;}

    int query(int x)
    {
        int ret=0;
        for (;x;x-=lowbit(x)) ret+=num[x];
        return ret;
    }
}t;

void build()
{
    for (int i=1;i<=n;i++)
    {
        while (top&&A[stack[top]]<A[i]) fa[i]=stack[top-1],fa[son[i][0]]=stack[top],son[stack[top]][1]=son[i][0],son[i][0]=stack[top],fa[stack[top--]]=i;
        if (top) son[stack[top]][1]=i;
        fa[stack[++top]=i]=stack[top-1];
        if (!fa[i]) root=i;
    }
}

int st[N][3];
int tp;
int vis[N];

void dfs()
{
    int x,l,r;
    st[++tp][0]=root,st[tp][1]=1,st[tp][2]=n;
    while (tp)
    {
        x=st[tp][0],l=st[tp][1],r=st[tp][2];
        if (!x)
        {
            tp--;
            continue;
        }
        if (!vis[x])
        {
            if (x-l<r-x)
                for (int i=l;i<=x;i++)
                {
                    qs[++q].x=r,qs[q].a=A[x]/A[i],qs[q].sign=1;
                    qs[++q].x=x,qs[q].a=A[x]/A[i],qs[q].sign=-1;
                    ans+=(i<x&&A[i]==1);
                }
            else
                for (int i=x;i<=r;i++)
                {
                    qs[++q].x=x-1,qs[q].a=A[x]/A[i],qs[q].sign=1;
                    qs[++q].x=l-1,qs[q].a=A[x]/A[i],qs[q].sign=-1;
                    ans+=(i>x&&A[i]==1);
                }
            vis[x]++;
        }
        else if (vis[x]==1)
        {
            vis[x]++;
            st[++tp][0]=son[x][0],st[tp][1]=l,st[tp][2]=x-1;
            continue;
        }
        else
        {
            vis[x]++;
            st[tp][0]=son[x][1],st[tp][1]=x+1,st[tp][2]=r;
        }
    }
}

void pre()
{
    for (int i=1;i<=n;i++) srt[i].key=A[i],srt[i].id=i;
    sort(srt+1,srt+1+n);
    idx=0;
    for (int i=1;i<=n;i++) num[lab[srt[i].id]=idx+=srt[i].key!=srt[i-1].key]=srt[i].key;
}

int pos(int v)
{
    int l=1,r=idx,mid,ret=0;
    while (l<=r)
    {
        mid=l+r>>1;
        if (num[mid]<=v) l=(ret=mid)+1;
        else r=mid-1;
    }
    return ret;
}

void calc()
{
    sort(qs+1,qs+1+q);
    for (int cur=1,i=1;i<=q;i++)
    {
        while (cur<=n&&cur<=qs[i].x) t.modify(lab[cur++],1);
        ans+=qs[i].sign*t.query(pos(qs[i].a));
    }
}

int main()
{
    freopen("arrpairs.in","r",stdin),freopen("arrpairs.out","w",stdout);
    n=read();
    for (int i=1;i<=n;i++) A[i]=read();
    build(),ans=0,dfs(),pre(),calc();
    printf("%lld\n",ans);
    fclose(stdin),fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值