ATB282H Min + Sum 学习笔记
ATB282H Min + Sum 学习笔记
题意简述
给定序列 \(A,B\)。求有多少对 \((l,r)\) 满足 \(1\le l\le r\le n\),且
\[\sum_{i=l}^r B_i+\min_{i=l}^r A_i \le S
\]
。
做法解析
显然极值分治,显然要启发式合并(枚举小的那个子树的缀,二分大的那个子树的缀能取到哪)。学过笛卡尔树就会。两个二分写完一个另一个复制过来改一改就行了。
代码实现
#include <bits/stdc++.h>
using namespace std;
namespace obasic{
typedef long long lolo;
template <typename _T>
void readi(_T &x){
_T k=1;x=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
x*=k;return;
}
template <typename _T>
void writi(_T x){
if(x<0)putchar('-'),x=-x;
if(x>9)writi(x/10);
putchar(x%10+'0');
}
};
using namespace obasic;
const int MaxN=2e5+5;
int N;lolo S,A[MaxN],B[MaxN],sum[MaxN],ans;
int stk[MaxN],ktp,ls[MaxN],rs[MaxN];
lolo getsum(int l,int r){return sum[r]-sum[l-1];}
lolo lbis(int u,int cl,int cr){
lolo res=0,lim=S-A[u];
for(int i=u;i>=cl;i--){
lim-=B[i];if(lim<0)break;
int lj=u,rj=cr,mj,sj;
while(lj<=rj){
mj=(lj+rj)>>1;
if(getsum(u+1,mj)<=lim)sj=mj,lj=mj+1;
else rj=mj-1;
}
res+=sj-u+1;
}
return res;
}
lolo rbis(int u,int cl,int cr){
lolo res=0,lim=S-A[u];
for(int i=u;i<=cr;i++){
lim-=B[i];if(lim<0)break;
int lj=cl,rj=u,mj,sj;
while(lj<=rj){
mj=(lj+rj)>>1;
if(getsum(mj,u-1)<=lim)sj=mj,rj=mj-1;
else lj=mj+1;
}
res+=u-sj+1;
}
return res;
}
lolo dfs(int u,int cl,int cr){
if(cl==cr)return A[u]+B[u]<=S;
lolo res=0;int lsiz=u-cl,rsiz=cr-u;
if(lsiz<=rsiz)res+=lbis(u,cl,cr);
else res+=rbis(u,cl,cr);
if(ls[u])res+=dfs(ls[u],cl,u-1);
if(rs[u])res+=dfs(rs[u],u+1,cr);
return res;
}
int main(){
readi(N),readi(S);
for(int i=1;i<=N;i++)readi(A[i]);
for(int i=1;i<=N;i++)readi(B[i]),sum[i]=sum[i-1]+B[i];
for(int i=1;i<=N;i++){
int k=ktp;
while(k&&A[stk[k]]>A[i])k--;
if(k)rs[stk[k]]=i;
if(k<ktp)ls[i]=stk[k+1];
stk[++k]=i,ktp=k;
}
ans=dfs(stk[1],1,N);writi(ans);
return 0;
}
反思总结
极为板的笛卡尔树。蓝的总结。
浙公网安备 33010602011771号