zoukankan      html  css  js  c++  java
  • 4408: [Fjoi 2016]神秘数

    4408: [Fjoi 2016]神秘数

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 452  Solved: 273
    [Submit][Status][Discuss]

    Description

    一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数。例如S={1,1,1,4,13},

    1 = 1

    2 = 1+1

    3 = 1+1+1

    4 = 4

    5 = 4+1

    6 = 4+1+1

    7 = 4+1+1+1

    8无法表示为集合S的子集的和,故集合S的神秘数为8。

    现给定n个正整数a[1]..a[n],m个询问,每次询问给定一个区间[l,r](l<=r),求由a[l],a[l+1],…,a[r]所构成的可重复数字集合的神秘数。

    Input

    第一行一个整数n,表示数字个数。
    第二行n个整数,从1编号。
    第三行一个整数m,表示询问个数。
    以下m行,每行一对整数l,r,表示一个询问。

    Output

    对于每个询问,输出一行对应的答案。

    Sample Input

    5
    1 2 4 9 10
    5
    1 1
    1 2
    1 3
    1 4
    1 5

    Sample Output

    2
    4
    8
    8
    8

    HINT

     

    对于100%的数据点,n,m <= 100000,∑a[i] <= 10^9

     

    Source

     

    【题解】:

        暴力做法:首先将这一段排序,然后如果存在某一个数,这个数比它前面的数的前缀和至少大2,那么答案就是它前面那个数的前缀和+1。
        然而我们要想优化,才可AC。

      如果我们将这段数排序,并且已知前n个数的神秘数为x,即现在凑得的数的区间为[1,x],新加入的数为a,那么不难发现,我们凑得的数又得到了一段区间[a+1,a+x],那么如果a+1<=x,我们就可以拼上这两段,而神秘数变为a+x+1。

      也即是说,我们有当前解ans,我们将所有小等ans的数加起来(其实根据前面所推应该是小于,但是写小等不会错,而且对于代码来说更好些,至于为什么不多赘述),如果sigma<ans说明出现了断裂处,即此时ans为答案。否则我们将ans变为sigma+1,继续更新答案。

      时间复杂度0(nlogn*P),其中P为常数(当数列为斐波那契时会被卡到极限40)

    题解转自网络。(毕竟太弱了。QAQ)

    【代码】:

    #include<cstdio>
    using namespace std;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x;
    }
    const int N=1e5+10,M=N*100;
    int n,m,sz,ans,root[N],ls[M],rs[M],sum[M];
    void insert(int &k,int last,int l,int r,int val){
        k=++sz;
        ls[k]=ls[last];
        rs[k]=rs[last];
        sum[k]=sum[last]+val;
        if(l==r) return ;
        int mid=l+r>>1;
        if(val<=mid) insert(ls[k],ls[last],l,mid,val);
        else insert(rs[k],rs[last],mid+1,r,val);
    }
    int query(int x,int y,int l,int r,int lim){
        int mid=l+r>>1;
        if(r<=lim) return sum[y]-sum[x];
        else if(lim<=mid) return query(ls[x],ls[y],l,mid,lim);
        else return sum[ls[y]]-sum[ls[x]]+query(rs[x],rs[y],mid+1,r,lim);
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++) insert(root[i],root[i-1],1,1e9,read()); 
        m=read();
        for(int i=1,l,r;i<=m;i++){
            l=read();r=read();ans=1;
            for(int sigma;(sigma=query(root[l-1],root[r],1,1e9,ans))>=ans;ans=sigma+1);
            printf("%d
    ",ans);
        }
        return 0;
    }

     

  • 相关阅读:
    我该不该学习C语言
    Java入门系列-27-反射
    Java入门系列-26-JDBC
    Java入门系列-25-NIO(实现非阻塞网络通信)
    Java入门系列-24-实现网络通信
    Java入门系列-23-NIO(使用缓冲区和通道对文件操作)
    Java入门系列-22-IO流
    Java入门系列-21-多线程
    Java入门系列-20-异常
    Java入门系列-19-泛型集合
  • 原文地址:https://www.cnblogs.com/shenben/p/6272499.html
Copyright © 2011-2022 走看看