题意:首先引入d()函数,d(n)=n+n的各位数字之和。比如d(75) = 75 + 7 + 5 = 87。一个数被称作self number当且仅当对所有的i<n,d(i) != n。(即n不存在generator)。
输入: N, K, s1...sk. (1<=N<=10^7, 1<=K<=5000) 。要求输出1~N的self number数量。然后依次输出第s1~第sk个self number(输入确保第sk个self number小于N)
思路:拿起来就模拟,像筛质数那样,开了一个10^7大小的flag数组,结果TLE。参考了别人的代码(http://www.cnblogs.com/Rinyo/archive/2012/12/04/2802337.html)发现可以用滚动数组。滚动数组以前我只在dp问题里见过,这还是第一次用在数学问题中,还是要灵活!!
滚动周期64是这样的来的:因为数不超过10^7,所以所有数的最大各位和为63(来自9999999),所以周期取64。这样做的依据是n < d(n) <= n+63
期间wa了两次是因为题目中的s1~sk并不是按照递增顺序给出的,所以必须另外加一些标志位来进行处理。
还知道了一个小技巧,对于此题求某个数各个位上的数之和,可以提前预处理好0~10000的各位之和d[i],对于任意数k,把这个数拆成两部分后分别求和,再相加即可。所以,d[k]=d[k/10000]+d[k%10000]
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 64
int flag[N];
int d[10001];
struct node{
int index,id,res;//index为输入的下标,id为输入的Sk,res保存值
}p[5005];
int num=0;
int digit(int x){
int res=0;
while(x){
res += x%10;
x /= 10;
}
return res;
}
void init(){//打表列出前10000个数的digit和
int i;
for(i = 1;i<=10000;i++)
d[i] = digit(i);
}
int next(int x){//求d(x)
int res = x;
res += d[x%10000];
res += d[x/10000];
return res;
}
int cmp1(const struct node *a,const struct node *b){//用于筛的过程中保存
return (*a).id-(*b).id;
}
int cmp2(const struct node *a,const struct node *b){//用于还原成输入的顺序
return (*a).index-(*b).index;
}
int main(){
int res = 0,i,n,m,now = 1;
freopen("a.txt","r",stdin);
init();
memset(flag,0,sizeof(flag));
scanf("%d %d",&n,&m);
for(i = 1;i<=m;i++){
scanf("%d",&p[i].id);
p[i].index = i;
}
qsort(p+1,m,sizeof(struct node),cmp1);
for(i = 1;i<=n;i++){
if(!flag[i%N]){//表示找到self number
num++;
while(now<=m && num == p[now].id)//万一输入的s1...k中还有相同的呢
p[now++].res = i;
}
flag[i%N] = 0;//筛完这个位置立即归零
flag[next(i)%64] = 1;
}
qsort(p+1,m,sizeof(struct node),cmp2);
printf("%d\n",num);
for(i = 1;i<=m;i++)
printf("%d ",p[i].res);
putchar('\n');
return 0;
}