ACM 1410 . 求算法

languagec 2007-10-24 12:14:02
http://acm.zju.edu.cn/show_problem.php?pid=1410
---------------------------------------------
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "math.h"

int main()
{
char arry[200000];
char temp[100];
int step[50000];
int n,i;
int cur,p;
unsigned int len,sum=0;


arry[0]=0; i=1; len=0;
while(sum<=2147483647)
{
sprintf(temp,"%d",i);
len=len+strlen(temp);
step[i]=len;
sum+=len;
strcat(arry,temp);
i++;
}

scanf("%d",&n);
while(n--)
{
scanf("%d",&i);

cur=0;
p=i;
while(p>0)
{
cur++;
p=p-step[cur];
}

p=p+step[cur]-1;

printf("%c",arry[p]);

}
return 0;
}
...全文
207 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
languagec 2007-10-24
  • 打赏
  • 举报
回复
本人的TL了
medie2005 2007-10-24
  • 打赏
  • 举报
回复
应是:
Sk含的数字位数可以表示成Y(k,0)+Y(k,1)+Y(k,2)+...+Y(k,9),记为T(k)。
medie2005 2007-10-24
  • 打赏
  • 举报
回复
忘记改了,应是:
在1,2,3,4,5,6,7,8,9,10,11,12中数字1出现的次数是1+1+2+1=5次。(分别在1,10,11,12处出现)
medie2005 2007-10-24
  • 打赏
  • 举报
回复
忘记改了,应是:
在1,2,3,4,5,6,7,8,9,10,11,12中数字1出现的次数是1+1+2+1=4次。(分别在1,10,11,12处出现)
medie2005 2007-10-24
  • 打赏
  • 举报
回复
假设我们已经知道如何统计不大于n的自然数的组成中数字d(d=1,2,3,...,9)的出现次数,记为Y(n,d)。
为了便于理解这个概念,举个例子:
n=12 d=1。
那么。在1,2,3,4,5,6,7,8,9,10,11,12中数字1出现的次数是1+1+2+1=4次。(分别在1,10,11,12处出现)

显然,Sk含的数字位数可以表示成Y(k,1)+Y(k,2)+...+Y(k,9),记为T(k)。

由于i的最大值为2147483647,而Sk含的数字位数,绝对不小于k。
由于65536*65536>2*2147483647。因此,要找的第i个出现的数字,必定在k<65537的Sk中出现,考虑到k值较小,因此尝试枚举k。

方法如下:

定义一个整形变量sum,初始为0。
k=1,2,...依次计算T(k)(计算方法后面再讲),sum+=T(k)。若发现在某个k时,sum>i,那么,就说明第i个出现在Sk中,
现在只需要求在Sk中的第i-(sum-T(k))个数字就成了,由上面的分析,i-(sum-T(k))不会很大,直接枚举也可以,重复利用已算得的T(j)(j<k)也可以。

最后说说如何计算T(k).
计算T(k),就是计算Y(k,d).
计算Y(k,1)的方法如下:

对任一自然数逐位求。拿1617来说,从左至右逐位计算:在1--1617之间,首位为1的数有617+1个;
在1--1617之间次位为1的数有(1+1)*100个;在1--1617之间第三位为1的数有16*10+17+1个;
在1--1617之间末位为1的数有161+1个。
总结规律:
对任意自然数n,从左至右逐位定为1,求其余为的所有组合数,相加即为f(n)。同时根据上面的计算过程
知:
1)如果n的k位是1,就采取将k位之前的数字组成的数(如果没有,置为0)乘以10^(k-1)(即10的k-1次方)再加上k位以后 的数字组成的数加一。
2)如果n的k位是0,就采取将k位之前的数字组成的数乘以10^(k-1)。
3)如果n的k位是其他的数,就采取将k位之前的数字组成的数(如果没有,置为0)加一乘以10^(k-1)。
hnsdxujunyi06 2007-10-24
  • 打赏
  • 举报
回复
数学唯美思想
感受优越人生
超级大笨狼 2007-10-24
  • 打赏
  • 举报
回复
先向参加ACM集训的同学表示敬意!~~
虽然比你们空活了10年+基本功还是比较差
超级大笨狼 2007-10-24
  • 打赏
  • 举报
回复
ACM训练还真够BT的,一代后狼推前狼啊

一般要做到50行以内的程序不用调试、100行以内的二分钟内调试成功.acm主要是考算法的
,主要时间是花在思考算法上,不是花在写程序与debug上。
下面给个计划你练练:

第一阶段:
练经典常用算法,下面的每个算法给我打上十到二十遍,同时自己精简代码,
因为太常用,所以要练到写时不用想,10-15分钟内打完,甚至关掉显示器都可以把程序打
出来.
1.最短路(Floyd、Dijstra,BellmanFord)
2.最小生成树(先写个prim,kruscal要用并查集,不好写)
3.大数(高精度)加减乘除
4.二分查找. (代码可在五行以内)
5.叉乘、判线段相交、然后写个凸包.
6.BFS、DFS,同时熟练hash表(要熟,要灵活,代码要简)
7.数学上的有:辗转相除(两行内),线段交点、多角形面积公式.
8. 调用系统的qsort, 技巧很多,慢慢掌握.
9. 任意进制间的转换

第二阶段:
练习复杂一点,但也较常用的算法。
如:
1. 二分图匹配(匈牙利),最小路径覆盖
2. 网络流,最小费用流。
3. 线段树.
4. 并查集。
5. 熟悉动态规划的各个典型:LCS、最长递增子串、三角剖分、记忆化dp
6.博弈类算法。博弈树,二进制法等。
7.最大团,最大独立集。
8.判断点在多边形内。
9. 差分约束系统.
10. 双向广度搜索、A*算法,最小耗散优先.

第三阶段:
前两个阶段是打基础,第三阶段是锻炼在比赛中可以快速建立模型、想新算法
。这就要平时多做做综合的题型了。
1. 把oibh上的论文看看(大概几百篇的,我只看了一点点,呵呵)。
2. 平时扫扫zoj上的难题啦,别老做那些不用想的题.(中大acm的版主经常说我挑简单的来
做:-P )
3. 多参加网上的比赛,感受一下比赛的气氛,评估自己的实力.
4. 一道题不要过了就算,问一下人,有更好的算法也打一下。
5. 做过的题要记好 :-)

tailzhou 2007-10-24
  • 打赏
  • 举报
回复
设:
f1(k)=len(1 & 2 & .... & k);
f2(k)=len(f1(1)& f1(2) & .... & f1(k)) ;
那么:
f1(9)=9;
f1(99)=f1(1)+90*2;
f1(999)=f1(2)+900*3;
f1(9999)=f1(3)+9000*4;
f2(9)=45;
f2(99)=f2(9)*90+(1+2+3+....90)*2;
f2(999)=f2(99)*900+(1+2+3+....900)*3;
f2(9999)=f2(999)*9000+(1+2+3+....9000)*4;

对于范围[1,9],[10,99],[100,999],[1000,9999],[10000,99999]
f1,f2可以表示为函数,比如:
f1(i)=f1(9)+(i-9)*2 10<=i<=99;
f2(i)=f2(9)*i+(1+2...+(i-9))*2 10<=i<=99;

那么对于要求解的字符C;
由f2可以计算出该字符所在的f(k)以及其索引;
由f1可以计算出该字符所在的k以及其索引;








超级大笨狼 2007-10-24
  • 打赏
  • 举报
回复
麻烦楼主把题目翻译一下,虽然6级过了n年,还是没看明白题目,俺不想花时间在阅读这种英文上.
medie2005 2007-10-24
  • 打赏
  • 举报
回复
我上面的方法其实还可以优化。

“k=1,2,...依次计算T(k)(计算方法后面再讲),sum+=T(k)。若发现在某个k时,sum >i,那么,就说明第i个出现在Sk中,”

由于T(k)是递增的,因此,上面的过程可以用折半查找来实现。这样的话,复杂度就很低了。

另:计算Y(k,i)的方法可以有计算y(k,1)的方法类比得到。
C1053710211 2007-10-24
  • 打赏
  • 举报
回复
居然是这个题!!!
pku 1019的discuss里有详细讨论,你自己去看看吧
http://acm.pku.edu.cn/JudgeOnline/bbs?problem_id=1019

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧