【noip冲刺赛】:循环整数

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

把一个区间[l,r]分成两个区间[1,l][1,r],然后分别计算各区间的数量,最后用右边的区间减去左边的区间即可。

至于中间查找的过程= =。。。我能说是找规律吗。。

#include<cstdio>//将一个区间[l,r]分为[1,l]和[1,r]; 分别计算值 然后右边的减去左边的 
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=19;

char a[20];
char b[20];
int n;
long long num[20];
long long sum[20];


void prepare()//初始 
{
	for(int i=1;i<maxn;i++)
	{
		long long p=9;
		for(int j=1;j<i;j++,p*=10)
		{
			
			if(i%j==0)
			{
				sum[j]=p;
			//	printf("%d\n",p);
				for(int k=1;k<j;k++)
				{
					if(j%k==0)
					{
						sum[j]-=sum[k];//
					}
				}
				num[i]+=sum[j];
			//	printf("i=%d j=%d num=%lld sum=%lld\n",i,j,num[i],sum[j]);
			}
		
		}
	}
}
long long init(char a[])//字符串转化为数字
{
	long long rs=0;
	int n=strlen(a);

	for(int i=0;i<n;i++)
	{
		rs=rs*10+a[i]-'0';
	}
	return rs;
}
int count_num(long long x)
{
	int d[maxn];
	int p=0;//p即为位数 
	while(x>0)//将每一位存到d里面 
	{
		d[++p]=x%10;
		x/=10;
	}
	int ans=0;
	for(int i=1;i<p;i++)
	{
		ans+=num[i];
	//	printf("i=%d num=%lld ans=%d\n",i,num[i],ans);
	}
	long long s=1;
	for(int j=1;j<p;j++,s*=10)
	{
		if(p%j==0)
		{
			long long t=0;
	    	for(int i=p;i>p-j;i--)//
	    	{
	    		t=t*10+d[i];
	    		//printf("t=%d\n",t);
	    	}
	    	long long q=0;
	    	bool flag=false;
	    	for(int i=p-j;i>0;i--)
	    	{
	    	//	printf("i=%d\n",i);
	    		q=q*10+d[i];//
	    	//	printf("%d\n",q);
	    		if(i%j==1||j==1)
	    		{
	    			if(q<t)
	    			{//printf("--------------");
	    				flag=true;
	    			}else if(q>t) break;
	    			q=0;
	    		}
	    	}
	    	sum[j]=t-s-flag+1;
	    //	printf("j=%d orz=%d\n",j,sum[j]);
	    	for(int k=1;k<j;k++)
	    	{
	    		if(j%k==0)
	    		{
	    			sum[j]-=sum[k];
	    		}
	    	}
	    	ans+=sum[j];
		}
	}
	return ans;
}
int main()
{
	freopen("circulate.in", "r", stdin);
	freopen("circulate.out", "w", stdout);
	prepare();
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s %s",a,b);
		long long sb1=init(a);
		long long sb2=init(b);
	//	printf("%lld %lld\n",sb1,sb2);
		long long ans=count_num(sb2)-count_num(sb1-1);
		//printf("sb2=%d sb1=%d\n",count_num(sb2),count_num(sb1));
		printf("%lld\n",ans);
	}
	return 0;
}
/*
求一个数字区间内有循环节的数字个数:
input:
3
1 10000
55555 66666
10 100 

output:
108
2
9
*/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值