题意
给出一个序列,问有多少个区间满足:最大值的两倍小于等于区间和
题解
分治的思想,也是笛卡尔树
对于所有的最大值,看多少个包含它的区间满足要求
先在区间中找到最大值的下标
如果左区间长度较小,我们则枚举左端点在左区间的,在右区间二分找到合适的右端点
如果右区间长度较小,则枚举右端点在右区间
总复杂度为
注意用 时的边界条件
代码
#include<bits/stdc++.h> #define N 300010 #define INF 0x3f3f3f3f #define eps 1e-10 // #define pi 3.141592653589793 #define mod 97 #define P 1000000007 #define LL long long #define pb push_back #define fi first #define se second #define cl clear #define si size #define lb lower_bound #define ub upper_bound #define bug(x) cerr<<#x<<" : "<<x<<endl #define mem(x) memset(x,0,sizeof x) #define sc(x) scanf("%dd",&x) #define scc(x,y) scanf("%d%d",&x,&y) #define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z) using namespace std; #include <ext/pb_ds/assoc_container.hpp> #include <ext/pb_ds/hash_policy.hpp> using namespace __gnu_pbds; int a[N],lg[N]; LL s[N],ans; int f[N][19]; inline int idx(int l,int r){ int t=lg[r-l]; if (a[f[l][t]]>a[f[r-(1<<t)+1][t]]) return f[l][t];else return f[r-(1<<t)+1][t]; } void work(int l,int r){ if (l>=r) return; if (l+1==r){ if (a[l]==a[r]) ans++; return; } int k=idx(l,r),t;LL M=a[k],tm=0,la; if (k-l>r-k){ for(int i=k;i<=r;i++){ tm+=a[i]; la=M*2-tm; if (s[i]-s[l-1]<M*2) continue; if(la<=0) t=k;else { t=lb(s+l,s+k+1,s[k-1]-la)-s; if (la==s[k-1]-s[t]) t++; } ans+=t-l+1; } }else{ for(int i=k;i>=l;i--){ tm+=a[i]; la=M*2-tm; if (s[r]-s[i-1]<M*2) continue; if (la<=0) t=k;else t=lb(s+k,s+r+1,s[k]+la)-s; ans+=r-t+1; } } work(l,k-1); work(k+1,r); } int main(){ for(int i=1;(1<<i)<N;i++) lg[1<<i]=1; for(int i=1;i<N;i++) lg[i]+=lg[i-1]; int T; sc(T); while(T--){ int n; sc(n); for(int i=1;i<=n;i++) sc(a[i]),s[i]=s[i-1]+a[i],f[i][0]=i; for(int j=1;(1<<j)<=n;j++) for(int i=1;i<=n;i++) if (i+(1<<j)-1<=n) if (a[f[i][j-1]]>a[f[i+(1<<j-1)][j-1]]) f[i][j]=f[i][j-1];else f[i][j]=f[i+(1<<j-1)][j-1]; ans=0; work(1,n); printf("%lld\n",ans); } }