笛卡尔积

https://ac.nowcoder.com/acm/contest/883/G

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define LL long long
const int maxn=3e5+5;
int a[maxn];
int rt,ls[maxn],rs[maxn];
int top;
int sta[maxn];
int fa[maxn];

int build(int n) //笛卡尔树 大顶堆
{
    top=0;
    for(int i=1;i<=n;i++)
    {
        while(top&&a[sta[top-1]]<a[i])top--; //小数都出栈
        if(!top) //栈空 i点作右上父亲
        {
            int t=sta[0];
            fa[t]=i;
            ls[i]=t;
        }
        else //i点可做右孩子
        {
            int t=sta[top-1];
            if(rs[t]){
                ls[i]=rs[t];
                fa[rs[t]]=i;
            }
            fa[i]=t;
            rs[t]=i;
        }
        sta[top++]=i;
    }
    return sta[0]; //根结点

}

LL pre[maxn];
void dfs(int rt,int l,int r,LL &ans){
    if(l>=r)return;
    if(rt-l<r-rt){
        // 找右边第一个可以的位置
        rep(i,l,rt){
            int pos=lower_bound(pre+rt,pre+r+1,pre[i-1]+2ll*a[rt])-pre;
//            if(pos==i)pos++;
//            if(pos==r+1)continue;
            ans+=(LL)(r-pos + 1);
        }
    }
    else{
        rep(i,rt,r){
            // 找左边第一个不行的位置
            int pos=upper_bound(pre+l,pre+rt,pre[i]-2ll*a[rt])-pre;
            if(pre[i]-pre[pos-1]>=2*a[rt]) pos++;
            pos=min(pos,rt+1);
            ans += (LL)(pos - (l - 1));
//            ans+=max(0,pos-l);
        }
    }

    dfs(ls[rt],l,rt-1,ans);
    dfs(rs[rt],rt+1,r,ans);
}

int main(){
    int t;scanf("%d",&t);
    while(t--){
        int n;scanf("%d",&n);
        rep(i,1,n){
            scanf("%d",&a[i]);
            pre[i]=pre[i-1]+1ll*a[i];
        }
        rt = build(n);
        LL ans=0;
        dfs(rt,1,n,ans);
        printf("%lld
",ans);
    }
}
原文地址:https://www.cnblogs.com/downrainsun/p/11262230.html