Atcoder Beginner Contest 363 D题题解

Tips:观看本篇题解之前,请确保你已经注册了Atcoder账号注册了洛谷账号,以便查看题目


题目传送门

Atcoder 原题(出自Atcoder Beginner Contest 363)Atcoder Beginner Contest 363 D - Palindromic Numbericon-default.png?t=N7T8https://atcoder.jp/contests/abc363/tasks/abc363_d

 如果你没有Atcoder账号,也可以在洛谷查看题目

洛谷 ABC363 D - Palindromic Numbericon-default.png?t=N7T8https://www.luogu.com.cn/problem/AT_abc363_d


声明

本题解仅代表个人思路,如有异议请及时指出,谢谢orz

1.题意描述

本题就是在求第 n 小的回文数,要注意第 1 小的回文数为 0.

2.整体思路

(1) 自定义说明

先确定回文串长度的一半 len(我们定义为回文长度),举个例子:

当 n 为 14 时,很显然此时第 14 小的回文串为 44 ,长度的一半为 11,那么 len=1;

特别地,当回文串长度 s 为奇数时,我们记该回文串的 len=(s+1)/2,方便我们后续求倒序的操作

当 n 为 46 时,此时第 46 小的回文串为 363 ,长度的一半为 2,那么 len=2.


(2) 构思过程

首先,我们可以从 1 开始枚举 i ,目标是寻找到 n 的回文串的回文长度 len ,在每次的枚举中,计算满足 len=i 时 n 的个数,即记录有 k 个回文串满足 len==i ,很显然 k 可以递推出来

递推公式:枚举 i 时,我们同时记录一个变量 s ,初始 s=0 ,在每次循环中都让 s+=1,当 s==2 时,说明距离上次 s==0 已经过去两次了,那么我们再记录一个变量 base ,方便下面的计算,用来表示回文串的前半部分为 base+c(c为常数且c<=base) , 此时应该把回文串的前半部分(也就是我们枚举的 base )的值乘上 10 ,容易证明回文串的前半部分(长度为len)在 s==0 和 s==1 时是一样的(有且仅有最后一位是否重复2次,那么分两种情况讨论:

当 s!=2 时:k[i]=k[i−1];

当 s==2 时:k[i]=k[i−1]∗10;

根据这两个递推式,我们即可计算出 k 数组的前缀和,k[j] 表示有多少个回文长度小于等于 j 的回文串,当 k[j]>=n 时,说明答案的 len 一定小于等于 j .

那么显然,k[j]−base 也就是答案的 len 的回文串第 p 个最小的 ,那么 第 n 个最小的应该是 n−(k[j]−base)

最后我们再根据 n 的 len 的奇偶性来输出后半部分的回文串即可

其实我们也可以不用记录 len,只需要根据 s 的取值来判断 len 的奇偶性即可

3.代码实现

示例代码

#include <bits/stdc++.h>
using namespace std;
unsigned long long n,k,s,tot,base;
int main()
{
	cin >> n;
	tot = 1;
	base = 9;
	while (1)
	{
		tot += base;
		if (tot >= n)
		{
			tot -= base;
			break;
		}
		s++;
		if (s == 2)
		{
			s = 0;
			base *= 10;
		}
	}
	base /= 9;
	k = n-tot+base-1;
	cout<<k;
	if (s == 0)
	{
		k /= 10;
	}
	while (k)
	{
		cout<<k%10;
		k/=10;
	}
	return 0;
}
  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值