sgu108



SGU108 Self-numbers II
题目大意:
1949年印度数学家D.R.Kaprekar发现了一个称为“自我数”的数。对于任意正整数n,定义d(n)为n再加上它的各位数字之和。例如,d(75)=75+7+5=87。给定任意n作为开始点,你可以构造出一个无穷增长序列,d(n),d(d(n)),d(d(d(n))),.... 例如,你以33开始,下一个数是33+3+3=39,再下一个是39+3+9=51,再下一个是51 +5+1=57,继续如此就可以产生如如下递增序列:33,39,51,57,69,84,96,111,114,120,123,129,141,...数字n被称为d(n)的产生器 。在上述的序列中,33为39的产生器,39为51的产生器, 51为57的产生器,等等。有些数有超过一个的产生器: 例如,101有两个产生器,91和100。一个数如果没有产生器就是一个自我数。让a[i]表示第i个自我数。有13个数a[1]..a[13]小于100:1,3,5,7,9,20,31,42,53,64,75,86,和97.(第一个自我数[1]=1,第二个自我数a[2]=3,.., 第十三个自我数a[13]=97);
输入
输入包含整数N,K,s1...sk.(1<=N<=10^7, 1<=K<=5000)用空格或者空行分开。
输出
第一行打印一个数--在区间[1..N]内所包含的“自我数”的总数。
第二行包含K个数-a[s1]..a[sk],用空格分开。输入保证所有的自我数a[s1]..a[sk]在区间[1..N]内。(例如当N=100,sk可以为1..13而不能为14,因为第14个自我数a[14]=108,108>100)
样例输入
100 10
1 2 3 4 5 6 7 11 12 13
样例输出
13
1 3 5 7 9 20 31 75 86 97

显然这是一道模拟题,找到d()构造不出来的。
首先,1肯定是无法构造的。
其次,N越大答案越大。
再者,对于任意的正整数i,满足d(i)>i。
OK,运用以上的性质,我们会发现,排除d()可以构造的显然会比直接找要快。
回顾SGU102,我们可以使用一种类似欧拉筛的方法,即用i排除d[i]。
(SGU102可以参考:http://blog.csdn.net/tgop_knight/article/details/40618505)
大致思路就是这样。

对内存的优化:
    10^7的bool数组显然会MLE(SGU的4M小空间就是坑)
    其实研究一下就会发现,check[]是可以滚动的,因为check[]存在性质:
        1.数字i(1<=i<=10^7)的数字和小于64(原因自己猜)。
        2.数字i在筛过d(i)之后就失去了作用。
    完美解决内存问题。

对时间的优化:
    1.对数字和的优化:
    对于数字i(1<=i<=10^7),它的数字和(num[i])显然满足:
        num[i]=num[i/10000]+num[i%10000];(加法交换律)
    预处理出num[1~10000]就可以优化每一步的数字和。
    (其实这个优化没什么用。。。就算暴力求解数字和也能在0.25s内通过,有兴趣的OIer可以写写(我就偷懒了哈))
    2.对求解K个答案的优化
    显然,每次找到答案后在K个询问下寻找显然会超时(时间复杂度O(N*K),SGU的0.25s小时限坑死你)
    我们可以对K个询问进行排序,因为询问小的必定会先得到解(原因很简单吧...)
    排序方法随意(比如我的冒泡排序法都可以过...)   

注意事项:
    这个答案的输出一定要按题目给的顺序啊!不是排序后的顺序啊!没注意的话就等着WA在TEST6吧。
    至于PE这个问题,你就把最后的回车删掉试试(我反正成功了)
   
下面附上我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdbool.h>
int n,k,t;
int total;
int order[5010],a[5010];
int num[5010];
bool check[110];
int maked(int a)
{
  int sum=0;
  while (a!=0)
    {
	sum+=a%10;
	a/=10;
	}
  return sum;
}
void delet()
{
  int i,j,l;
  t=1;
  for (i=1;i<=n;i++)
    {
    if (!check[i%64])
      {
      total++;
      while (t<=k && a[order[t]]==total)
        {
        num[order[t]]=i;
        t++;
        }
      }
    l=i+maked(i);
    check[l%64]=true;
    check[i%64]=false;
    }
  return ;
}
void init()
{
  int i,j,m;
  scanf("%d%d",&n,&k);
  for (i=1;i<=k;i++)
    {
    scanf("%d",&a[i]);
    order[i]=i;
	}
  for (i=1;i<k;i++)
    for (j=i+1;j<=k;j++)
      if (a[order[i]]>a[order[j]])
        { m=order[i]; order[i]=order[j]; order[j]=m; }
  delet();
  printf("%d\n",total);
  for (i=1;i<=k;i++)
    {
    printf("%d",num[i]);
    if (i!=k)
      printf(" ");
    }
  return ;
}
int main()
{
  init();
  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值