• BZOJ 4408: [Fjoi 2016]神秘数


    4408: [Fjoi 2016]神秘数

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 553  Solved: 343
    [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

    分析:

    很机智的想法...貌似之前见过类似的想法...然而并没有想到..QAQ~~~

    考虑当前我们有一个数字集合可以表示区间$[1,x]$内的数字,考虑新加入一个数字$y$,分以下两种情况:

    $1.$  $y<=x+1$,那么我们发现新的数字集合可以表示区间$[1,x+y]$内的所有数字

    $2.$  $y>x+1$,那么$x+1$这个数字不能被表示出来,所以$x+1$就是答案...

    所以对于每个区间,我们先选定$ans=1$作为空集的神秘数,也就是说当前的集合可以表示区间$[0,ans-1]$,然后把区间内所有小于等于$ans$的数字全部加起来得到$sum$,如果$sum<ans$,那么就代表不存在新的小于等于$ans$的数字,那么就代表$ans$就是当前区间的神秘数,查询$sum$可以主席树维护...

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    //by NeighThorn
    #include<map>
    using namespace std;
    
    const int maxn=100000+5,maxm=7000000+5;
    
    int n,m,id,tot,a[maxn],mp[maxn],ls[maxm],rs[maxm],val[maxn],root[maxn];
    
    long long ans,tmp,sum[maxm];
    
    inline bool cmp(int x,int y){
    	return a[x]<a[y];
    }
    
    inline void change(int l,int r,int x,int &y,int pos,int val){
    	y=++tot;sum[y]=sum[x]+val;
    	if(l==r) return;
    	int mid=(l+r)>>1;ls[y]=ls[x],rs[y]=rs[x];
    	if(pos<=mid)
    		change(l,mid,ls[x],ls[y],pos,val);
    	else
    		change(mid+1,r,rs[x],rs[y],pos,val);
    }
    
    inline long long query(int l,int r,int y,int L,int R){
    	if(l==L&&r==R) return sum[y];
    	int mid=(l+r)>>1;
    	if(R<=mid)
    		return query(l,mid,ls[y],L,R);
    	else if(L>mid)
    		return query(mid+1,r,rs[y],L,R);
    	else
    		return query(l,mid,ls[y],L,mid)+query(mid+1,r,rs[y],mid+1,R);
    }
    
    signed main(void){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]),mp[i]=i,val[i]=a[i];
    	sort(mp+1,mp+n+1,cmp);sort(val+1,val+n+1);
    	for(int i=1;i<=n;i++)
    		change(1,n,root[i-1],root[i],mp[i],a[mp[i]]);
    	scanf("%d",&m);
    	for(int i=1,l,r;i<=m;i++){
    		scanf("%d%d",&l,&r);ans=1;
    		while(9524){
    			id=upper_bound(val+1,val+n+1,ans)-val-1;
    			tmp=query(1,n,root[id],l,r);
    			if(tmp<ans) break;
    			else ans=tmp+1;
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

      


    By NeighThorn

  • 相关阅读:
    MyBatis代码自动生成
    英语单词--程序员专属
    动态规划小例子
    Dijkstra 算法
    矩阵变换
    (转)贝塞尔曲线
    山寨了@上位者的怜悯的样式= =
    HDU 1115 (计算多边形重心)
    HDU 1147(线段相交+链表)
    HDU 1276 (直接链表模拟)
  • 原文地址:https://www.cnblogs.com/neighthorn/p/6653139.html
Copyright © 2020-2023  润新知