思路:使用暴力算法每隔 3000000 个数字求答案,打出一张表,再使用类似分块的算法求解。
打表代码:
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
template<typename T> inline T read() {
T X = 0; bool flag = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') flag = 0; ch = getchar();}
while (ch >= '0' && ch <= '9') {X = (X << 1) + (X << 3) + ch - '0'; ch = getchar();}
if (flag) return X;
return ~ (X - 1);
}
template<typename T> inline void write(T X) {
if (X < 0) {putchar('-'); X = ~ (X - 1);}
int s[50], top = 0;
while (X) {s[++top] = X % 10; X /= 10;}
if (!top) s[++top] = 0;
while (top) putchar(s[top--] + '0');
putchar(',');
putchar(' ');
return;
}
inline int S(int n) { //S 函数
int ans = 0;
while (n) {
ans += n % 10;
n /= 10;
}
return ans;
}
inline bool check(int n) {
if (S(n * n) == S(n) * S(n)) {
return true;
}
return false;
}
signed main() {
for (int l = 1, r = 3000000; r <= 1000000000; l += 3000000, r += 3000000,
(r > 1000000000 && r < 1003000000) ? (r = 1000000000) : (r = r)) {
//从 l = 1, r = 3000000 开始循环,每次 l 和 r 加 3000000,如果超过了 1000000000 就把块的右边界移至 1000000000
int ans = 0;
for (int i = l; i <= r; i++) {
ans += check(i);
}
write(ans);
}
putchar('\n');
return 0;
}
提交的代码:
#include <cstdio>
#include <iostream>
#define int long long
#define pl(n) (n / 3000000)
using namespace std;
const int len = 3000000;
int anss[505] = {
1373, 60, 0, 967, 183, 0, 303, 242, 0, 1, 114, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1011,
202, 0, 471, 470, 0, 1, 312, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 353, 294,
0, 1, 311, 0, 0, 171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 120, 0,
0, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
template <typename T>
inline T read() {
T X = 0;
bool flag = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') flag = 0;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
X = (X << 1) + (X << 3) + ch - '0';
ch = getchar();
}
if (flag) return X;
return ~(X - 1);
}
template <typename T>
inline void write(T X) {
if (X < 0) {
putchar('-');
X = ~(X - 1);
}
int s[50], top = 0;
while (X) {
s[++top] = X % 10;
X /= 10;
}
if (!top) s[++top] = 0;
while (top) putchar(s[top--] + '0');
putchar('\n');
return;
}
inline int S(int n) { //S 函数
int ans = 0;
while (n) {
ans += n % 10;
n /= 10;
}
return ans;
}
inline bool check(int n) {
if (S(n * n) == S(n) * S(n)) {
return true;
}
return false;
}
signed main() {
int l = read<int>(), r = read<int>();
int ans = 0;
if (pl(l) == pl(r)) { //如果左端点和右端点在同一个块内,暴力解决
for (int i = l; i <= r; i++) {
ans += check(i);
}
} else { //否则,使用类似分块的放大解决
for (register int i = l; pl(i) == pl(l); ++i) ans += check(i);
for (register int i = pl(l) + 1; i <= pl(r) - 1; i++) ans += anss[i];
for (register int i = r; pl(i) == pl(r); --i) ans += check(i);
}
write(ans);
return 0;
}
此代码只有 30 分;通过乱改表中数字可得 90 分代码,但是没有理论依据。
求求了,帮萌新调个题呗