bzoj 4408: [FJOI2016]神秘数 主席树

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值