最小不能表示正整数
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);
}
}