笛卡尔积
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\n",ans); } }