先看一下对于给定的一段如何暴力求最大值,首先将这一段排序,然后如果存在某一个数,这个数比它前面的数的前缀和至少大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