luogu P4587 [FJOI2016]神秘数

我们首先考虑暴力如何做。

现在最大的问题就是如何找出这个神秘数

先按升序排序,然后一个一个加数。

假设现在的值域是[1,pos],当前要加入的数是 x ;

不难发现,有两种情况:

  若x>pos+1 , 那么一定拼不出pos+1,答案即为pos+1;

  若1<= x <= pos+1 ,  那么现在的值域就变成了[1,pos+x];

 这是最暴力的方法。。

优化:

考虑一段区间能够做出的贡献。

假设当前的值域为[1,pos],mx为上次用来更新值域时统计到的最大的范围 , 那么现在能做出贡献的一定有[mx+1,pos+1],统计出这段区间的sum。

(mx及之前的就不用统计了啦,因为在上一次更新值域时已经加过了)

现在的值域就变成了[1,pos+sum] , mx就变成了pos+1 ;

若sum==0,则说明[mx+1,pos+1]这段区间里没有数,那么答案就是pos+1啦(因为后面的数一定大于pos+1)。。

额,至于要快速找某段区间的某个范围的和,当然是主席树,然后就木有了。

Code

 

#include<iostream>
#include<cstdio>
#define N 100010
using namespace std;
int n,m,a[N],tot,root[N];
const int INF=1e9;
struct Node{
    int lc,rc,sum;
}t[5000000];
inline int read(){
    char c=getchar();int x=0,flag=1;
    while(c<'0' || c>'9') {if(c=='-') flag=-1;c=getchar();}
    while(c>='0' && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
    return x*flag;
}
int insert(int q,int l,int r,int x,int val){
    int p=++tot;t[p]=t[q];if(l==r){t[p].sum+=val;return p;} 
    int mid=(l+r)>>1;
    if(x<=mid) t[p].lc=insert(t[q].lc,l,mid,x,val);
    else t[p].rc=insert(t[q].rc,mid+1,r,x,val);
    t[p].sum=t[t[p].lc].sum+t[t[p].rc].sum;
    return p;
}
int Query(int p,int q,int l,int r,int ls,int rs){
    if(ls<=l && rs>=r) return t[p].sum-t[q].sum;
    int mid=(l+r)>>1,val=0;
    if(ls<=mid) val+=Query(t[p].lc,t[q].lc,l,mid,ls,rs);
    if(rs>mid) val+=Query(t[p].rc,t[q].rc,mid+1,r,ls,rs);
    return val;
} 
int main(){
    n=read();
    for(int i=1;i<=n;i++){a[i]=read();root[i]=insert(root[i-1],1,INF,a[i],a[i]);}
    m=read();
    while(m--){
        int l=read(),r=read(),mx=0,pos=0,sum=0;
        while(true){
            sum=Query(root[r],root[l-1],1,INF,mx+1,pos+1);
            if(!sum){printf("%d\n",pos+1);break;}
            mx=pos+1;pos+=sum;
        }
    }
    return 0;
}

 

posted @ 2019-10-17 09:24  dzzx_Syh  阅读(126)  评论(1编辑  收藏  举报