20180530模拟赛T2——绀碧之棺

题目背景

qiancl 得到了一张藏宝图,上面写了一道谜题。

题目描述

定义\(F(n)\)为 n 在十进制下各个数位的平方和,求区间\([a,b]\)中有多少\(n\)满足\(k\times F(n) = n\)

输入描述

一行三个正整数\(k,a,b\)

输出描述

一行一个整数表示满足条件的\(n\)的个数。

样例输入

51 5000 10000

样例输出

3

数据范围及提示

测试数据 对应数据范围
其中12个测试点 \(1\le k,a,b\le 105\)
其中6个测试点 \(233\le k\le 250\),且\(1\le a,b\le10^8\)
剩下32个测试点 \(1\le k,a,b\le 10^{18}\)

样例中满足的\(3\)\(n\)分别是\(7293,7854,7905\)

题解

开始被\(10^{18}\)的数据范围给吓到了,%你赛结束后听说有人打了数位dp(代码看这里)。orz

然而正解是枚举😄。

开始我没想那么多,就打了个普通的枚举,然后开始考虑优化。

首先,不难发现\(k\mid n\),于是我们就可以让\(n\) k个k个地加。

然后我们就考虑一下缩小枚举区间:

首先\(n\)是要求的,无法考虑在此方向拓展;

\(k\)于是我们就想到\(F(n)\),考虑到\(n \le 10^{18}\),又因为题目中明确说明“在十进制下”,于是我们就不难发现\(F(n)\le 9^2\times 18 = 1458\)(每一位都取\(9\))。又因为我们是\(k\)\(k\)位枚举的,所以我们最多只需枚举\(1458\)次。

注意开long long

代码

#include <fstream>
#include <algorithm>

using namespace std;

ifstream fin("coffin.in");
ofstream fout("coffin.out");

typedef long long LL;

LL a, b, k;

const int sqr[] = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81};
const int kk = 81*18;

inline LL F(LL x)
{
	LL ans = 0;
	while(x)
	{
		ans += sqr[x%10];
		x /= 10;
	}
	return ans;
}

inline bool check(LL n)
{
	return F(n)*k == n;
}

int main()
{
	int ans = 0;
	fin >> k >> a >> b;
	if(a == b)
	{
		fout << check(a);
		return 0;
	}
	LL l = (a%k) ? ((a/k)+1)*k : a;
	LL r = min((kk*k), b);
	for(LL i = l; i <= r; i += k)
		if(check(i))
			ans++;
	fout << ans << '\n';
	return 0;
}
posted @ 2018-05-30 19:23  pfy_pfy  阅读(131)  评论(0编辑  收藏  举报