【11.2noip冲刺赛】 循环整数 (分段打表)

【问题描述】
moreD在学习完循环小数之后发现循环是个很美好的性质。自己只需要记住短短的循环节以及循环次数(次数大于1,且是整数)就可以记住整个数字了。
因为背诵数字变得方便了,moreD决定背诵[L,R]内的所有循环的整数。moreD的背诵计划有T天,但是他不知道每天具体要背多少个数,请你帮助moreD计算出每天需要背诵的数字个数。
如果moreD在某天遇到一个曾经背过的数字,他会义无反顾地重新背诵。
【输入格式】 第一行给出一个整数T,表示moreD计划背诵T天的数字。 接下来n行,第i行给出2个整数Li,Ri,表示moreD第i天的背诵计划。
【输出格式】 输出T行,每行一个整数,表示第i天moreD需要背诵的数字个数。
【输入输出样例】
circulate.in
3
1 10000
55555 66666
10 100

circulate.out
108
2
9
【数据范围】
对于30%的数据 T*MAX{Ri}<=2*10^6
对于70%的数据MAX{Ri}<=2*10^6
对于100%的数据 T<=50000,1<=Li<=Ri<=2*10^18
【样例解释】
对于第2天,moreD只需要背诵55555,66666.
对于第3天,moreD只需要背诵11,22,33,44,55,66,77,88,99

(╯‵□′)╯︵┻━┻10的18次方!!

先让我们最容易想到的思路是 :算出1-r的循环数个数,再算出1-l的循环数个数,两者相减就完了;

如何求出1到一个数中循环数的数量,可以用到分治的思想:求一到n的循环数的个数,得到n的长度 length,把它转换成求1-10的length次方中的数量(此处有规律,打表)+10的length次方-n的数量;

比如求1-xxxx(某四位数)中的循环数个数,先求1-1000中的个数(即18),再求1000-xxxx的个数。xxxx可以划分为xx|xx(两个数字为一循环节)且都可以取到10-xx之间的数。因此1000-xxxx中间的循环数的个数=xx-10;所以总的循环数的个数为18+xx-10;

在尝试这个思路时,有一个细节要处理,就是第一个循环节在之后可能取不到(如1-123111,我们取不到123123这个循环数),这时需要将第一个循环节的大小加1;

代码如下(╯‵□′)╯︵┻━┻【因为还没测过,正确性有待商榷】

╮(╯▽╰)╭反正我自己的数据都是对的……

ヾ(?`Д´?)楼上滚蛋!赶紧去测啊喂!

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=50001;
int t;
long long f[20];
long long xunhuanjie[20];
int l_num[maxn],r_num[maxn];
long long ans[maxn];
int right,left;
void init()
{
    freopen("circulate.in","r",stdin);
    freopen("circulate.out","w",stdout);
}
long long work(int a[],int l)
{
    int d[19];
    long long ans=0;
    for(int i=1;i<l;i++) ans+=f[i];//1到l这个区间最少的循环数个数,作为基数记录下来;
    long long num=1;
    for(int i=1;i<l;i++)
    {

        if(l%i==0)//如果i长度的循环节满足;
        {
            long long p=0;
            long long q=0;
            int w=0;
            for(int j=1;j<=i;j++) q=q*10+a[j];//记录第一个循环节的大小;
            for(int j=i+1;j<=l;j++)
            {
                p=p*10+a[j];//记录第一个循环节以后的循环节大小;
                if(j==l||j%i==1)
                {
                    if(p>q) break;//如果后面有任意一个循环节比第一个循环节小,则不能取到循环节,如123111,我们取不到循环节123(循环数为123123),此时让循环节加一,如果能取到,退出;
                     else if(p<q) w=1;
                    p=0;
                }
            }
            xunhuanjie[i]=q-num-w+1;//循环节减去循环的基数;
            for(int j=1;j<i;j++)
            if(i%j==0){
             xunhuanjie[i]-=xunhuanjie[j];//减去重复的循环节;
                 }
            ans+=xunhuanjie[i];
        }
        num=num*10;
    }
    return ans;
}
void  biao()//初始化,算出1到10的次方的区间的循环数个数;
{
    for(int i=1;i<18;i++)
    {
        long long num=9;
        for(int j=1;j<i;j++)//枚举循环节长度;
        {
            if(i%j==0){
                xunhuanjie[j]=num;
                for(int k=1;k<j;k++) if(j%k==0) xunhuanjie[j]-=xunhuanjie[k];//减去重复的循环节;
                f[i]+=xunhuanjie[j];
            }
            num=num*10;
        }
    }
}
void read()
{
    scanf("%d",&t);
    for(int j=1;j<=t;j++)
    {
        char l[19],r[19];
        scanf("%s",l);
        scanf("%s",r);
        left=strlen(l);
        right=strlen(r);
        for(int i=1;i<=strlen(l);i++) l_num[i]=l[i-1]-‘0‘;
        for(int i=1;i<=strlen(r);i++) r_num[i]=r[i-1]-‘0‘;
        ans[j]=work(r_num,right)-work(l_num,left);
    }
}
int main()
{
    //init();
    biao();
    read();
    for(int i=1;i<=t;i++) printf("%lld\n",ans[i]);
    return 0;
}
时间: 2024-08-06 11:57:13

【11.2noip冲刺赛】 循环整数 (分段打表)的相关文章

2017.6.11 校内模拟赛

题面及数据及std(有本人的也有原来的) :2017.6.11 校内模拟赛 T1 自己在纸上模拟一下后就会发现 可以用栈来搞一搞事情 受了上次zsq 讲的双栈排序的启发.. 具体就是将原盘子大小copy一下排个序 用两个指针维护两个数组(原数据 和 排序后的数据), 即分为1数据和2数组 将小于1指针指向的数据的2数组中的数据全部压入栈中 后进行消除, 将栈栈顶元素与当前1数组中的1指针指向的元素进行比较 相同则消除 后重复过程 直至指针超过N 后判断一下是否两个指针都超过了N... #incl

省赛之一冲刺赛

2016-05-9 冲刺赛题目是从70~~80题 http://acm.nefu.edu.cn/JudgeOnline/problemShow.php?problem_id=1170 总结: 题做的并不理想,在五个小时的时间里只做出来四道水题,原因有很多. 其一,英语不好翻译不了作者所要表达的意思. 其二,知识点不了解,比如对于位运算并不知道——虽然在百度之后把题做了出来 关于位运算http://blog.chinaunix.net/uid-21411227-id-1826986.html 其三

11年湖南省赛 B counting game

比赛的时候这道题卡了大半时间,虽然说其他题目也挺多不会的.昨天一直觉得自己代码是没问题的,今早起来想想,可能是题意读错了,特意去看了一遍中文题意,恍然大悟. 第一:题意为只要  “含7的,或是7的倍数” 就拍手,比赛时被看成了尾数是7就拍手. 第二:今早交一次又WA了,原因是判断每位数是不是7的地方出了问题. 本人原码:   if(num % 7 == 0 || num % 10 == 7 || num / 10 == 7 || num / 100 == 7 || num / 1000 == 7

20.10 for循环 20.11/20.12 while循环 20.13 break跳出循环 20.14 continue结束本次循环 20.15 exit退出整个脚本

20.10 for循环 ?语法:for 变量名 in 条件; do -; done ? 案例1 1+2+3..+100的和 #!/bin/bash sum=0 for i in `seq 1 100` // seq 1到100个数字 do sum=$[$sum+$i] echo $i done echo $sum sum 第一次作为变量的时候,是0:当进入for循环里面的时候,每运算一次,sum变量就会改变一次,直至$i 结束:最后输出结果 $sum ? 案例2 文件列表循环 #!/bin/ba

20.10 for循环 20.11/20.12 while循环 20.13 break跳出循环 20

20.10 for循环语法:for 变量名 in 条件; do -; done案例1#!/bin/bashsum=0for i in seq 1 100do? ? sum=$[$sum+$i]? ? echo $idoneecho $sum文件列表循环#!/bin/bashcd /etc/for a in ls /etc/do? ? if [ -d $a ]? ? then? ?? ? ls -d $a? ? fidone 20.11/20.12 while循环语法 while 条件; do -

2019.11.30训练赛总结

2019.11.30训练赛总结 Codeforces Round #499 (Div. 2) 总的来说是一场很不愉快的比赛.漏洞百出. 对于A题,其实没有什么技术含量,只是写的时候忘记了边界的情况,导致出现错误. B题,一定程度上考验了思维,既然从正面做不行,那么我可以反着来,既然求不可以正向求出答案,那我可以把答案枚举带进去看是否符合条件啊,如果数据范围再大点的话还可以二分. D题,也反应出了自己的一个漏洞,就是懒得一步一步去推数据,代数据进去.当出现bug的时候,最直接,最有效的办法,就是把

Harmonic Number LightOJ - 1234 (分段打表)

题意: 求调和级数,但n很大啦.. 解析: 分段打表  每间隔50存储一个数,在计算时  只需要找到离输入的n最近的那个数 以它为起点 开始计算即可 emm...补充一下调和级数的运算公式   r为常数,r=0.57721566490153286060651209(r就是欧拉常数). 看一下这位的博客:https://www.cnblogs.com/weiyuan/p/5737273.html #include <iostream> #include <cstdio> #inclu

简单的for循环实现九九乘法表

PHP for 循环 语法 for (init counter; test counter; increment counter) { code to be executed; } 参数: init counter:初始化循环计数器的值 test counter:: 评估每个循环迭代.如果值为 TRUE,继续循环.如果它的值为 FALSE,循环结束. increment counter:增加循环计数器的值 实例: 下面的例子显示了从 0 到 10 的数字: <?php for ($x=0; $x

for循环实现九九乘法表

<!--for循环实现九九乘法表--> <table border="1"> <tbody> {% for x in range(1,10) %} <tr> {% for y in range(1,x + 1) %} <td> {{ y }} * {{ x }} = {{ y * x }} </td> {% endfor %} </tr> {% endfor %} </tbody> <