模拟/sgu108 Self-numbers II
题意
定义d(n):d(n)=n+[n的各位数之和]
如果某个数不能由一个数通过多次d(n)得到这个数,那么称这个数为“自我数”
比如:d(12)=12+1+2=15
d(d(12))=d(15)=15+1+5=21
d(d(d(12)))=d(d(15))=d(21)=21+2+1=24
…………
则15、21、24等数都是可以得到的,不是“自我数”
而例如20,则就不能通过由某个数多次d(n)得到,所以20为一个“自我数”
现在给定一个n和m,求1~n中“自我数”的个数 并且输出其中第a[i]个(i∈[1,m])“自我数”
分析
不难想到,通过筛法,一层一层向下算d(n),能得到的数打上标记,这样可以求出的
然而这样会超内存,因为你需要开一个10^7的bool数组
所以就理所当然地想到了滚动数组
因为7位数,相加最大得到64,所以只需要64一滚动即可。
这里有个小技巧,对于求某个数各个位上的数之和,我们可以提前预处理好0~10000的各位之和sum[i]
对于任意一个数k,把这个数拆成两部分后分别求和,再相加即可
所以,sum[k]=sum[k/10000]+sum[k&10000]
至此,本题解决
注意在数组滚动的过程中初始化
Accepted Code
1 /* 2 PROBLEM:sgu108 3 AUTHER:Rinyo 4 MEMO:枚举 筛法 滚动数组 5 */ 6 7 #include<cstdio> 8 #include<cstring> 9 #include<algorithm> 10 using namespace std; 11 12 int n,m; 13 int answer[10030],sum[10030]; 14 bool f[100]; 15 16 struct rec 17 { 18 int data,num,ans; 19 }a[10030]; 20 21 int cmp(rec x,rec y) {return x.data<y.data;} 22 23 int main() 24 { 25 26 scanf("%d%d",&n,&m); 27 28 for (int i=1;i<=m;i++){scanf("%d",&a[i].data);a[i].num=i;} 29 sort(a+1,a+m+1,cmp); 30 31 for (int i=0;i<=10000;i++) 32 { 33 int k=i; 34 sum[i]=0; 35 while (k>0) 36 {sum[i]+=k%10;k/=10;} 37 } 38 39 memset(f,true,sizeof(f)); 40 int tot=0,len=1; 41 for (int i=1;i<=n;i++) 42 { 43 if (f[i%64]) 44 { 45 tot++; 46 while ((a[len].data==tot) && (len<=m)) {a[len].ans=i;len++;} 47 } 48 f[i%64]=true; 49 int next=i+sum[i/10000]+sum[i%10000]; 50 f[next%64]=false; 51 } 52 53 for (int i=1;i<=m;i++) answer[a[i].num]=a[i].ans; 54 printf("%d\n",tot); 55 for (int i=1;i<m;i++) printf("%d ",answer[i]); 56 printf("%d\n",answer[m]); 57 58 return 0; 59 }