zoukankan      html  css  js  c++  java
  • 【积累】最小不能表示正整数 (以及一些做法

    最小不能表示正整数

    1,题:little w and Exchange

    题意:给定两个整数 (n)(m) ,以及一个长度为 (n) 的数组 (A) , 询问对于所有正整数 (wleq m) ,是否都能用数组 (A) 中的数表示,即说对于所有正整数 (wleq m) ,是否存在子数组 (Ssubseteq A)(S) 内所有元素的和恰好等于 (w) 。是的话输出 YES ,否则输出 NO(1leq nleq1000,\,1leq mleq2^{31}-1,\,1leq a_ileq2^{31}-1)

    解:对数组从小到大排序后,求前缀和,如果下一个数的数值 (a_i>sum_{i-1}+1) ,则最小不能表示数为 (sum_{i-1}+1) ,否则 ([1,sum_i]) 的数均能由子数组 (A_{1,2,dots,i}) 表示,需继续判断下一个数 。

    证明:

    假设该结论正确,则对于排序后的数组由: 子数组 (A_{1,2,dots,i}) 表示任意值不超过 (sum_{i-1}) 的正整数。

    当加入一个新的数 (a_i),如果这个数 (a_i>sum_{i-1}+1) ,显然 (a_i) 无法与任意零个或多个 (a_j(j<i)) 加和得到 (sum_{i-1}+1) 。反之,如果 (a_ileq sum_{i-1}+1) ,则区间 ([sum_{i-1}+1,sum_i]) 之内的正整数都能由 (a_i) 与任意零个或多个 (a_j(j<i)) 加和得到,因为 (0leq sum_{i-1}+1-a_i< sum_i-a_ile sum_{i-1}) 能被表示。

    而当 (i=1,sum_{i-1}=0) ,显然只有 (a_i=1) ,才能表示 (sum_{i-1}+1=1) ,否则,最小不能表示数则为 (1)

    结论正确。

    假如生成一个最优序列,则当 (n=31) 时,就能表示出 ([1,2^{31}]) 只能的所有正整数了。(考虑2进制数的01表示,当表示 (2^{31}) 内的数,只需要31位 )。

    代码:

    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    using namespace std;
    typedef long long ll;
    
    int a[maxn];
    int main()
    {
    	int n,m,x;
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    	sort(a+1,a+1+n);
    	ll sum=0;
    	for(int i=1;i<=n;i++)
    		if(a[i]<=sum+1)sum+=a[i];
    		else break;
    	if(sum>=m)puts("YES");
    	else puts("NO");
    }
    

    2,题:牛牛的凑数游戏

    题意:给定两个正整数 (n,m) ,一个长度为 (n) 的数组 (A)(m) 个询问。对于每个询问的 (l,r) 回答在子数组 (A[l,r]) 中,最小不能表示正整数是什么?(1leq n,mleq10^5,\,1leq a_ileq10^9,\,1le lle rle n)

    解:对于每个询问,一开始设 (sum=0), 每次把区间 ([l,r]) 内小于等于 (sum+1) 的数且尚未的数加进 (sum) ,如果 (sum) 的值在这次没有发生变化,则答案就为 (sum+1)总共需要迭代的次数其实仅为 (log\,n)

    对于区间内的求值可以用数据结构来实现。

    叨叨:这种乍一看瞧不出的时间复杂度,仔细一想确是很有道理。要习惯这种体型以及思维方式=w=。

    代码:

    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    using namespace std;
    typedef long long ll;
    const int maxn=2e5+5;
    const int inf=0x3f3f3f3f;
    
    
    int a[maxn];
    
    ll h[maxn];int cnt;
    inline int id(ll v){return lower_bound(h+1,h+1+cnt,v)-h;}
    
    
    int T[maxn],L[maxn<<4],R[maxn<<4],tot;ll sum[maxn<<4];
    void update(int&rt,int pre,int l,int r,int x,int v)
    {
    	rt=++tot;
    	sum[rt]=sum[pre]+v;
    	L[rt]=L[pre];R[rt]=R[pre];
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	if(x<=mid)update(L[rt],L[pre],l,mid,x,v);
    	else update(R[rt],R[pre],mid+1,r,x,v);
    }
    ll query(int st,int ed,int l,int r,int ql,int qr)
    {
    	if(ql>r||qr<l||st==ed)return 0;
    	if(ql<=l&&r<=qr)return sum[st]-sum[ed];
    	int mid=(l+r)>>1;
    	return query(L[st],L[ed],l,mid,ql,qr)+query(R[st],R[ed],mid+1,r,ql,qr);
    }
    
    int main()
    {
    	int n,m,l,r;
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]),h[++cnt]=a[i];
    	sort(h+1,h+1+cnt);
    	cnt=unique(h+1,h+1+cnt)-h-1;
    	for(int i=1;i<=n;i++)update(T[i],T[i-1],1,cnt,id(a[i]),a[i]);
    	ll sum,lsum;int k,lk;
    	while(m--)
    	{
    		scanf("%d%d",&l,&r);
    		sum=0;lsum=-1;lk=0;
    		while(lsum!=sum)
    		{
    			lsum=sum;
    			k=lower_bound(h+lk,h+1+cnt,sum+1)-h;
    			if(k>cnt||h[k]>sum+1)k--;
    			if(lk==k)break;
    			sum+=query(T[r],T[l-1],1,cnt,lk+1,k);
    			lk=k;
    		}
    		printf("%lld
    ",sum+1);
    	}
    }
    
  • 相关阅读:
    关于软工文档规范性——国标文档
    关于游戏平衡性——王者荣耀野怪数值参考
    单元测试特性标签(结合代码)
    常用标签梳理
    图片文件的转换、加载及缩放显示
    认识ManualResetEvent
    HTML简介
    HTML基本结构
    15年,我在深圳
    为什么坚持,想一想当初
  • 原文地址:https://www.cnblogs.com/kkkek/p/13848514.html
Copyright © 2011-2022 走看看