CF997E Good Subsegments 题解
murder_drones · · 题解
本文节选自笔者的另一篇文章:关于一类子区间计和问题
这个做法不需要历史和。
先以子区间最大子段和之和为例讲解具体算法流程。内容有关联,请务必阅读完例题再继续。
单组询问
连续子区间计数 (Good Subsegments)
也就是本题。
这题单组询问有一种较为奇妙的转化:考虑将
这题套路的做法也要用到结论:区间合法当且仅当
设当前归并到
对于右端点在
对于右端点在
反向同理。
提交记录:https://mirror.codeforces.com/problemset/submission/997/301081018
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int INF=1e9+10;
int prec[2][maxn],sufc[2][maxn];
struct SGT{
int op;
struct node{
int l,r;
long long addv,addw;
long long sumv;
int maxv,maxcnt;
}tr[maxn<<2];
void pushup(int p){
tr[p].sumv=tr[p<<1].sumv+tr[p<<1|1].sumv;
tr[p].maxv=max(tr[p<<1].maxv,tr[p<<1|1].maxv);
tr[p].maxcnt=0;
if(tr[p<<1].maxv==tr[p].maxv) tr[p].maxcnt+=tr[p<<1].maxcnt;
if(tr[p<<1|1].maxv==tr[p].maxv) tr[p].maxcnt+=tr[p<<1|1].maxcnt;
}
void addf(int p,long long v,long long w){
if(tr[p].maxv!=v) return ;
tr[p].addv=v;tr[p].addw+=w;
tr[p].sumv+=w*tr[p].maxcnt;
}
void pushdown(int p){
if(tr[p].addw){
addf(p<<1,tr[p].addv,tr[p].addw);
addf(p<<1|1,tr[p].addv,tr[p].addw);
tr[p].addw=0;
}
}
void build(int p,int l,int r){
tr[p].l=l,tr[p].r=r;tr[p].addw=0;
if(l==r){
tr[p].sumv=0;tr[p].maxcnt=1;
if(op==0) tr[p].maxv=prec[0][tr[p].l];
else tr[p].maxv=sufc[1][tr[p].l];
return ;
}int mid=(tr[p].l+tr[p].r)>>1;
build(p<<1,l,mid);build(p<<1|1,mid+1,r);pushup(p);
}
void add(int p,int l,int r,long long v,long long w){
if(r<l) return ;
if(l<=tr[p].l&&tr[p].r<=r) return addf(p,v,w);
int mid=(tr[p].l+tr[p].r)>>1;pushdown(p);
if(l<=mid) add(p<<1,l,r,v,w);
if(mid<r) add(p<<1|1,l,r,v,w);pushup(p);
}
void add(int p,int pos,long long v){
if(tr[p].l==tr[p].r) return void(tr[p].sumv+=v);
int mid=(tr[p].l+tr[p].r)>>1;pushdown(p);
if(pos<=mid) add(p<<1,pos,v);
else add(p<<1|1,pos,v);pushup(p);
}
long long query(int p,int l,int r){
if(r<l) return 0ll;
if(l<=tr[p].l&&tr[p].r<=r) return tr[p].sumv;
int mid=(tr[p].l+tr[p].r)>>1;pushdown(p);
if(r<=mid) return query(p<<1,l,r);
if(mid<l) return query(p<<1|1,l,r);
return query(p<<1,l,r)+query(p<<1|1,l,r);
}
}tr[2];
struct qry{
int l,r,id;
};long long ans[maxn];
int n,m;
int a[maxn];
int premax[maxn],sufmax[maxn];
int premin[maxn],sufmin[maxn];
long long cdq(int l,int r,vector<qry> q){
if(l==r){
for(qry p:q) ans[p.id]++;
return 1;
}int mid=(l+r)>>1;
for(qry &p:q) p.l=max(p.l,l),p.r=min(p.r,r);//!!!
// printf("cdq in:%d %d\n",l,r);
// for(qry p:q) printf("q:%d %d %d\n",p.l,p.r,p.id);
premax[mid]=0;premin[mid]=INF;
for(int i=mid+1;i<=r;i++){
premax[i]=max(premax[i-1],a[i]);
premin[i]=min(premin[i-1],a[i]);
prec[0][i]=premin[i]+i;
prec[1][i]=premax[i]-i;
}
sufmax[mid+1]=0;sufmin[mid+1]=INF;
for(int i=mid;i>=l;i--){
sufmax[i]=max(sufmax[i+1],a[i]);
sufmin[i]=min(sufmin[i+1],a[i]);
sufc[0][i]=sufmax[i]+i;
sufc[1][i]=sufmin[i]-i;
}
tr[0].build(1,mid+1,r);
tr[1].build(1,l,mid);
vector<qry> lq,rq;
for(qry p:q){
if(p.l<=l&&r<=p.r) continue;
if(p.l<=mid&&mid<p.r) lq.push_back(p),rq.push_back(p);
}
sort(lq.begin(),lq.end(),[](qry x,qry y){return x.l>y.l;});
sort(rq.begin(),rq.end(),[](qry x,qry y){return x.r<y.r;});
int p1=mid,p2=mid+1;
int lp=0,rp=0;
while(p1>=l&&p2<=r){
if(sufmax[p1]<=premax[p2]){
int p=lower_bound(premin+mid+1,premin+(p2-1)+1,sufmin[p1],greater<int>())-premin;
int np=sufc[0][p1]-sufmin[p1];
if(mid+1<=np&&np<p) tr[0].add(1,np,1);
tr[0].add(1,p,p2-1,sufc[0][p1],1);
while(lp<(int)lq.size()&&lq[lp].l>=p1){
ans[lq[lp].id]+=tr[0].query(1,mid+1,lq[lp].r);
lp++;
}p1--;
}else{
int p=upper_bound(sufmin+(p1+1),sufmin+mid+1,premin[p2])-sufmin-1;
int np=premin[p2]-prec[1][p2];
if(p<np&&np<=mid) tr[1].add(1,np,1);
tr[1].add(1,p1+1,p,prec[1][p2],1);
while(rp<(int)rq.size()&&rq[rp].r<=p2){
ans[rq[rp].id]+=tr[1].query(1,rq[rp].l,mid);
rp++;
}p2++;
}
}
while(p1>=l){
int p=lower_bound(premin+mid+1,premin+p2,sufmin[p1],greater<int>())-premin;
int np=sufc[0][p1]-sufmin[p1];
if(mid+1<=np&&np<p) tr[0].add(1,np,1);
tr[0].add(1,p,p2-1,sufc[0][p1],1);
while(lp<(int)lq.size()&&lq[lp].l>=p1){
ans[lq[lp].id]+=tr[0].query(1,mid+1,lq[lp].r);
lp++;
}p1--;
}
while(p2<=r){
int p=upper_bound(sufmin+(p1+1),sufmin+mid+1,premin[p2])-sufmin-1;
int np=premin[p2]-prec[1][p2];
if(p<np&&np<=mid) tr[1].add(1,np,1);
tr[1].add(1,p1+1,p,prec[1][p2],1);
while(rp<(int)rq.size()&&rq[rp].r<=p2){
ans[rq[rp].id]+=tr[1].query(1,rq[rp].l,mid);
rp++;
}p2++;
}
long long nowv=tr[0].query(1,mid+1,r)+tr[1].query(1,l,mid);
lq.clear(),rq.clear();
for(qry p:q){
int L=p.l,R=p.r;
if(L<=l&&r<=R) continue;
if(L<=mid) lq.push_back(p);
if(mid<R) rq.push_back(p);
}long long res=cdq(l,mid,lq)+cdq(mid+1,r,rq)+nowv;
for(qry p:q) if(p.l<=l&&r<=p.r) ans[p.id]+=res;
// printf("cdq out:%d %d\n",l,r);
// printf("res:%lld\n",res);
return res;
}
vector<qry> qr;
int main(){
scanf("%d",&n);tr[0].op=0;tr[1].op=1;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1,l,r;i<=m;i++) scanf("%d%d",&l,&r),qr.push_back({l,r,i});
cdq(1,n,qr);
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}