一些特殊的自然数可以表示成另一个自然数+这个数每一位的数字之和。现在要求出1--N中,有多少个数不是这样的数,并且给出K个下标,求1---N中从小到大第Ki个不能这样表示的数是多少...比如 8=4+4,15=12+1+2....这些都是可以表示的,但3,5,7...这样的数是无法表示出来的。。。
这题N虽然给了1e7的大小,但O(n)跑一遍是不会超时的,恩,,这题不卡时间,卡的是空间- -...空间给了4096KB,把1e7以内符合要求的数存下来是不可能的-但是可以利用1个64为整数来标记着64个数是否符合要求(每一位标记0,1即可)。。然后用cnt【i】存一下前i个64位整数里有多少个符合要求的数,第一问就直接出来了。。。对第二问,每个下标做一次二分查找,找到第Ki大的在哪个64位整数的第几位上就ok。
#include<iostream>
#include<cstdio>
#include<memory.h>
#include<algorithm>
#include<math.h>
using namespace std;
const int inf=0x3f3f3f3f;
typedef unsigned long long ll;
const int MOD=1e9;
const int maxn=1e7;
ll vis[160000+2000];
int cnt[160000+2000];
ll count(ll n)
{
ll c =0 ;
for (c =0; n; ++c)
{
n &= (n -1) ;
}
return c ;
}
ll n;
int num;
int m;
int k,p;
int slove(int num)
{
int low=0,high=1e7/63;
int mid;
while(low<high)
{
mid=(low+high)>>1;
if (cnt[mid]<num) low=mid+1;
else high=mid;
}
int p=num;
if (low>0) p-=cnt[low-1];
if (p==0) return 63*low;
for (int i=0; i<63; i++)
{
if ((vis[low] & 1LL<<(i))==0) p--;
if (p==0) return 63*low+i;
}
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
memset(vis,0,sizeof vis);
memset(cnt,0,sizeof cnt);
num=0;
for(ll i=1;i<=1e7;i++)
{
ll tmp=i;
int p,q;
int j=i;
while(j)
{
tmp+=j%10;
j/=10;
}
p=tmp/63;
q=tmp%63;
vis[p]|=(1LL<<(q));
}
vis[0]|=1;
num=1e7/63;
for (int i=0; i<=num; i++)
cnt[i]=count(vis[i]);
for (int i=0; i<=num; i++)
cnt[i]=63-cnt[i];
for (int i=1; i<=num; i++)
cnt[i]+=cnt[i-1];
scanf("%d%d",&m,&k);
int low=m/63;
int ans=0;
if (low>0) ans+=cnt[low-1];
m%=63;
for (int i=0; i<=m; i++)
if ((vis[low]& (1LL<<(i)))==0) ans++;
cout<<ans<<endl;
for (int i=1; i<k; i++)
{
scanf("%d",&p);
cout<<slove(p)<<" ";
}
scanf("%d",&p);
cout<<slove(p)<<endl;
return 0;
}