SPOJ10649 Mirror Number(三进制回文)

/*

题目:Mirror Number

题目描述:

A number is called a Mirror number if on lateral inversion, it gives the same number i.e it looks the same in a mirror. For example 101 is a mirror number while 100 is not. 

Given two numbers a and b, find the number of mirror numbers in between them (inclusive of a and b).

大意:求区间[a,b]内有多少个mirror数,mirror数是左右对称的,如0,1,8,11,88,101,111,181,808,818,888等,而其他的包含有非0,1,8数位的回文数则不是;

Input

First line contains T, number of testcases <= 10^5.
Each testcase is described in a single line containing two numbers a and b.
0 <= a<=b <= 10^44

Output

For each test case print the number of mirror numbers between a and b in a single line.

 

解题思路:

写一个求F(0,n)区间的mirror数的个数函数,那么答案就是F(b)- F(a-1);

F函数的思路如下:

第一步找出小于等于n,且是回文的数m;

第二步找出小于等于m,且只包含0,1,8,的数x;

第三步把0,1,8看成是三进制,类似于求区间回文数(只不过回文数里面可用的数字有10个,所以是10进制),统计奇数个位和偶数个位上的个数;

 

最后相加就是答案;

 

代码:

*/

 

 

#include<stdio.h>
#include<string.h>


#define LL long long
int ternary[10],vis[3]={8,1,0};

void getCloseNum(char *num)
{
	int i,j,tag;
	for(i=1;i<=num[0];i++)
	{
		tag=-1;
		for(j=0;j<3;j++)
		{
			if(num[i]==vis[j])break;
			if(tag<0&&num[i]>vis[j]) tag=j;
		}
		if(j>=3)
		{
			num[i++]=vis[tag];
			for(;i<=num[0];i++)
				num[i]=vis[0];
			break;
		}
	}
	if(tag==0){
		for(i=1;i<=num[0];i++)if(!num[i])tag++;else break;
		for(i=1;i<=num[0]-tag;i++)
			num[i]=num[i+tag];
		num[0]-=tag;
	}
}

LL cal(char *num)
{
	LL ans=0;
	int i;
	for(i=1;i<=num[0];i++)
	{
		ans*=3;
		ans+=ternary[num[i]];
	}
	return ans+1;
}

void subOne(char * num)
{
	int i,ans=1;
	for(i=num[0];i>0;i--)
	{
		num[i]-=ans;
		if(num[i]<0)num[i]+=10;
		else ans=0;
	}
	for(i=1;i<=num[0];i++) if(!num[i])ans++;else break;
	for(i=1;i<=num[0]-ans;i++)
		num[i]=num[i+ans];
	num[0]-=ans;
	if(num[0]==0)num[0]=1;
}

void Cp(char *a,char *b)
{
	int i;
	a[0]=b[0];
	for(i=1;i<=a[0];i++)a[i]=b[i];
}
LL fun(char *num,int tag)
{
	if(num[0]==0)return 0;
	getCloseNum(num);
	return cal(num)-(tag?0:1);
}
LL find(char *num)
{
	int i,tag;
	char nextbit[50];
	tag=num[0]&1;
	Cp(nextbit,num);
	num[0]=num[0]/2+(num[0]&1);
	for(i=1;i<=nextbit[0]-1;i++)
		nextbit[i]=9;
	nextbit[0]--;
	nextbit[0]=nextbit[0]/2+(nextbit[0]&1);
	return fun(num,tag)+fun(nextbit,1-tag);
}
void format(char *num,int has)
{
	int i,flag=0,tag;
	if(!has){
		for(i=1;num[i];i++)num[i]-='0'; num[0]=i-1;
	}
	tag=num[0]&1;
	for(i=1;i<=num[0]/2;i++)
	{
		if(num[num[0]/2+1-i]<num[num[0]/2+tag+i]) { flag=1;break;}
		if(num[num[0]/2+1-i]>num[num[0]/2+tag+i]) { flag=-1;break;}
	}
	if(flag<0)
	{
		for(i=2;i<=num[0]/2+tag;i++){
			if(num[i])break;
		}
		if(num[1]==1&&i>(num[0]/2+tag))
		{
			--num[0];
			for(i=1;i<=num[0];i++)
				num[i]=9;
		}
		else{
			num[0]=num[0]/2+tag;
			subOne(num);
			num[0]=2*num[0]-tag;
			for(i=num[0]/2+tag+1;i<=num[0];i++)
				num[i]=9;
		}
	}
}
int main()
{
	int cas;
	char a[50],b[50];
	ternary[8]=2;ternary[1]=1;ternary[0]=0;
	scanf("%d",&cas);
	while(cas--)
	{
		scanf("%s%s",a+1,b+1);
		format(a,0);
		format(b,0);
		if(a[1]==0&&a[0]==1){
			printf("%lld\n",find(b));
			continue;
		}
		subOne(a);
		format(a,1);
		printf("%lld\n",find(b)-find(a));
	}
	return 0;
}





 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
洛谷的SPOJ需要注册一个SPOJ账号并进行绑定才能进行交题。您可以按照以下步骤进行注册: 1. 打开洛谷网站(https://www.luogu.com.cn/)并登录您的洛谷账号。 2. 在网站顶部导航栏中找到“题库”选项,将鼠标悬停在上面,然后选择“SPOJ”。 3. 在SPOJ页面上,您会看到一个提示,要求您注册SPOJ账号并进行绑定。点击提示中的链接,将会跳转到SPOJ注册页面。 4. 在SPOJ注册页面上,按照要求填写您的用户名、密码和邮箱等信息,并完成注册。 5. 注册完成后,返回洛谷网站,再次进入SPOJ页面。您会看到一个输入框,要求您输入刚刚注册的SPOJ用户名。输入用户名后,点击“绑定”按钮即可完成绑定。 现在您已经成功注册并绑定了SPOJ账号,可以开始在洛谷的SPOJ题库上刷题了。祝您顺利完成编程练习!\[1\]\[2\] #### 引用[.reference_title] - *1* *3* [(洛谷入门系列,适合洛谷新用户)洛谷功能全解](https://blog.csdn.net/rrc12345/article/details/122500057)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [luogu p7492 序列](https://blog.csdn.net/zhu_yin233/article/details/122051384)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值