• FJOI2016 神秘数


    题目大意

    给定长为$N$一个序列,每次询问一个区间,求最小的不能表示为由区间内若干个(可以是$0$个)数的和的非负整数。

    考虑一个可重集合$S$,设抽取$S$中若干个数相加无法得到的最小非负整数为$Ans_S$

    显然$Ans_{emptyset}=1$

    当加入一个元素$x$时

    当$x>Ans_S$时,原先的$Ans_S$仍然无法凑出来,所以答案不变

    当$xleq Ans_S$时,原先的$0,1...Ans_S-1$可表示为$x,x+1...Ans_S-1+x$由于$xleq Ans_S-1$

    所以$S$加入$x$后得到的$K$一可以表示$0,1...Ans_S+x-1$,而$Ans_S+x$仍然无法凑出来

    所以$Ans_K=Ans_S+x$

    但是这样直接做还是会$TLE$,因为每次询问得把区间扫一遍。

    我们想一个很暴力的优化。

    设当前答案为$ans$,你已经加上了区间内小于等于$last$的数的和。

    由于答案已经达到了$ans$,我们求出$sum=$区间内满足$last<xleq ans$的所有$x$的和。

    若$sum=0$,则答案已经不再影响,直接停止就好,否则我们令$last=ans,ans=ans+sum$即可。

    初始时$last=0,ans=1$。

    这看起来很暴力,但是其中对于所有的若$sum>0$,则一定有$sum>last$,所以每两次操作$last$至少扩大一倍,而所有数的总和又是固定的,所以对于每一次询问$ans$只会进行$log$级别次数的增加。

    区间求一定值域内的和,用可持久化线段树维护即可。

    复杂度$O(Nlog(sum A_i)+Mlog^2(sum A_i))$

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define M 120000
    #define mid ((l+r)>>1)
    #define MAXN 1000000002
    using namespace std;
    int read(){
    	int nm=0,fh=1; char cw=getchar();
    	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    	return nm*fh;
    }
    int n,m,sum[M*32],L[M*32],R[M*32],rt[M],cnt;
    void write(int x){if(x>9) write(x/10); putchar(x-(x/10)*10+'0');}
    void ins(int &x,int pre,int l,int r,int pos){
    	x=++cnt,L[x]=L[pre],R[x]=R[pre];
    	sum[x]=sum[pre]+pos; if(l==r) return;
    	if(pos<=mid) ins(L[x],L[pre],l,mid,pos);
    	else ins(R[x],R[pre],mid+1,r,pos);
    }
    int qry(int now,int pre,int l,int r,int ls,int rs){
    	if(sum[now]==sum[pre]||r<ls||rs<l) return 0;
    	if(ls<=l&&r<=rs) return sum[now]-sum[pre];
    	return qry(L[now],L[pre],l,mid,ls,rs)+qry(R[now],R[pre],mid+1,r,ls,rs);
    }
    int main(){
    	n=read();
    	for(int i=1;i<=n;i++) m=read(),ins(rt[i],rt[i-1],1,MAXN,m);
    	for(int T=read(),last=0,ans=1;T;T--,last=0,ans=1){
    		int ls=read()-1,rs=read(),now=0;
    		while(true){
    			now=qry(rt[rs],rt[ls],1,MAXN,last+1,ans);
    			if(!now) break; last=ans,ans+=now;
    		} write(ans),putchar('
    ');
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    JVM学习笔记-指向Class类的引用(A Reference to Class Class)
    JVM学习笔记-方法区示例与常量池解析(Method Area Use And Constant Pool Resolution)
    JVM学习笔记-堆(Heap)
    JVM学习笔记-程序计数器(The Program Counter)
    JVM学习笔记-栈(Stack)
    JVM学习笔记-栈帧(The Stack Frame)
    JVM学习笔记-局部变量区(Local Variables)
    html大文件传输源代码
    html大文件传输源码
    html大文件传输插件
  • 原文地址:https://www.cnblogs.com/OYJason/p/9743208.html
Copyright © 2020-2023  润新知