dtoj#4263. duliu
题目描述:
小 D 喜欢出毒瘤题毒人。当然,他的毒瘤更多体现在若干个难题组合在同一场比赛时。
小 D 脑中有 $n$ 个毒瘤题 idea,第 $i$ 个的毒值为$d_i$。当第 $i$ 个题和第 $j$ 个题同时出现在一场比赛中,会产生$f(i,j) = d_i + d_j$ 的毒性。
小 D 决定用这些题在 YLOJ 上办 $m$ 场在线比赛。
• 由于这个 OJ 还在~~咕咕~~开发,YLOJ Extremelyhard Round #i 选取的题目编号集合只能为 $[l_i,r_i]$ 的一个非空子区间 $[a_i,b_i]$。
• 因为这个 round 是 extremelyhard 的,所以需要满足$\sum\limits_{a_i \le j,k \le b_i} f(j,k) \ge x_i$。
• 不过,为了防止题目过分毒瘤而被喷,小 D 希望最小化$max_{a_i \le j \le b_i} \{d_i\}$,而你需要告诉他这个最小值。
算法标签:线段树
思路:
对于一个区间的答案,可以分为三类,一类是以区间的左端点作为选择的区间作为左端点,一类是以区间的右端点作为选择区间的右端点,一类是左右端点在区间中间,受某个最大值的限制而不能扩大区间。
因为权值都是正数,所以对于一个选择的区间如果固定左端点,恰好满足总和限制的最小的区间最优,根据这个可以先二分计算出第一类和第二类的答案。
考虑第三类,我们对于每一个点求出它作为最大值可以延伸的最大区间。然后当这个区间被询问区间包含的时候,可以对答案造成贡献,但是这样是三维的。我们会发现如果你选择左端点在询问左端点和这个询问第二类求的答案区间的左端点左侧,这个时候你的答案一旦超过询问区间,这个答案一定没有单独取右区间优,所以只需要满足左端点在范围内,区间权值大于询问。是一个二维偏序,可以用线段树维护得到答案。
以下代码:
#include<bits/stdc++.h> #define il inline #define LL long long #define _(d) while(d(isdigit(ch=getchar()))) using namespace std; const int N=3e5+5,inf=1e9; int n,m,a[N],res[N],tot,Lg[N],st[N][20]; LL sum[N];int mn[N<<2]; struct node{ int l,r,ll,id;LL val; }t[N]; struct data{ int l,r,mx;LL val; }g[N]; il LL read(){ LL x,f=1;char ch; _(!)ch=='-'?f=-1:f;x=ch^48; _()x=(x<<1)+(x<<3)+(ch^48); return f*x; } bool cmp(node t1,node t2){ return t1.val>t2.val; } bool cmp1(data t1,data t2){ return t1.val>t2.val; } il int getmax(int l,int r){ if(l>r)swap(l,r);int k=Lg[r-l+1]; return max(st[l][k],st[r-(1<<k)+1][k]); } il int getl(int x,int v){ int l=1,r=x,res=x; while(l<=r){ int mid=(l+r)>>1; if(getmax(mid,x)<=v)res=mid,r=mid-1; else l=mid+1; } return res; } il int getr(int x,int v){ int l=x,r=n,res=x; while(l<=r){ int mid=(l+r)>>1; if(getmax(mid,x)<=v)res=mid,l=mid+1; else r=mid-1; } return res; } il LL cal(int l,int r){ return (sum[r]-sum[l-1])*(r-l+1)*2; } il int gl(int ql,int qr,LL v){ int l=ql,r=qr,res=ql; while(l<=r){ int mid=(l+r)>>1; if(cal(ql,mid)>=v)res=mid,r=mid-1; else l=mid+1; } return res; } il int gr(int ql,int qr,LL v){ int l=ql,r=qr,res=qr; while(l<=r){ int mid=(l+r)>>1; if(cal(mid,qr)>=v)res=mid,l=mid+1; else r=mid-1; } return res; } il void insert(int x,int l,int r,int pos,int v){ if(l==r){mn[x]=min(mn[x],v);return;} int mid=(l+r)>>1; if(pos<=mid)insert(x<<1,l,mid,pos,v); else insert(x<<1|1,mid+1,r,pos,v); mn[x]=min(mn[x<<1],mn[x<<1|1]); } il int query(int x,int l,int r,int ql,int qr){ if(ql<=l&&r<=qr)return mn[x]; int mid=(l+r)>>1;int res=inf; if(ql<=mid)res=query(x<<1,l,mid,ql,qr); if(mid<qr)res=min(res,query(x<<1|1,mid+1,r,ql,qr)); return res; } int main() { n=read();m=read(); for(int i=1;i<=n;i++)st[i][0]=a[i]=read(),sum[i]=sum[i-1]+a[i]; for(int i=2;i<=n;i++)Lg[i]=Lg[i>>1]+1; for(int j=1;j<=Lg[n];j++)for(int i=1;i+(1<<j)-1<=n;i++) st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]); for(int i=1;i<=n;i++){ int l=getl(i,a[i]),r=getr(i,a[i]); LL v=(sum[r]-sum[l-1])*(r-l+1)*2; g[i]=(data){l,r,a[i],v}; } for(int i=1;i<=m;i++){ int l=read(),r=read();LL x=read(); if((sum[r]-sum[l-1])*(r-l+1)*2<x)res[i]=-1; else{ int ll=gl(l,r,x),rr=gr(l,r,x); t[++tot]=(node){l,r,rr,i,x}; res[i]=min(getmax(l,ll),getmax(rr,r)); } } sort(t+1,t+1+tot,cmp);sort(g+1,g+1+n,cmp1); int now=1; for(int i=1;i<=n*4;i++)mn[i]=inf; for(int i=1;i<=tot;i++){ while(g[now].val>=t[i].val){ insert(1,1,n,g[now].l,g[now].mx),now++; } res[t[i].id]=min(res[t[i].id],query(1,1,n,t[i].l,t[i].ll)); } for(int i=1;i<=m;i++)printf("%d\n",res[i]); return 0; }