先看一下对于给定的一段如何暴力求最大值,首先将这一段排序,然后如果存在某一个数,这个数比它前面的数的前缀和至少大2,那么答案就是它前面那个数的前缀和+1。
那么假设现在处理了前面较小的一些数之后的答案为ans,然后可以求出比ans小的数的总和t,如果t<ans,那么答案就是ans;否则将ans更新为t+1。可以发现两次更新之后ans必然翻倍,那么更新的次数为O(logΣA),注意到可以用主席树维护这个和,那么每次查询就是O(logN)。因此总的时间复杂度为O(MlogNlogΣA)。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define M 2000005 using namespace std; int n,cnt,trtot,a[N],num[N],rt[N],sum[M],ls[M],rs[M]; int read(){ int x=0; char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar(); while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x; } int find(int x){ int l=1,r=cnt+1,mid; while (l+1<r){ mid=(l+r)>>1; if (num[mid]<=x) l=mid; else r=mid; } return l; } void ins(int l,int r,int x,int &y,int u,int v){ y=++trtot; sum[y]=sum[x]+v; if (l==r) return; int mid=(l+r)>>1; if (u<=mid){ rs[y]=rs[x]; ins(l,mid,ls[x],ls[y],u,v); } else{ ls[y]=ls[x]; ins(mid+1,r,rs[x],rs[y],u,v); } } int qry(int x,int y,int z){ int l=1,r=cnt,mid,ans=0; x=rt[x-1]; y=rt[y]; while (l<r){ mid=(l+r)>>1; if (z<=mid){ r=mid; x=ls[x]; y=ls[y]; } else{ l=mid+1; ans+=sum[ls[y]]-sum[ls[x]]; x=rs[x]; y=rs[y]; } } return ans+sum[y]-sum[x]; } int main(){ n=read(); int i,l,r,ans,t; for (i=1; i<=n; i++) a[i]=num[i]=read(); n++; sort(num+1,num+n+1); cnt=1; for (i=2; i<=n; i++) if (num[i]!=num[cnt]) num[++cnt]=num[i]; for (i=1; i<=n; i++) a[i]=find(a[i]); for (i=1; i<=n; i++) ins(1,cnt,rt[i-1],rt[i],a[i],num[a[i]]); int cas=read(); while (cas--){ l=read(); r=read(); for (ans=1; ; ans=t+1){ t=qry(l,r,find(ans)); if (t<ans) break; } printf("%d\n",ans); } return 0; }
by lych
2016.3.30