P17747 题解
该题解为本题的官方题解。
报数
设
直接计算这个求和表达式,时间复杂度会达到
不妨设
但是这样计算,时间复杂度仍有
考虑在每个完全平方数处将数轴切割成片段。
容易看出,当
于是可以证明,当
下面用归纳法证明:
- 当
r = 0 时,显然成立。 - 当
1 \le r < (q+1)^2 - q^2 时,假设f(q^2 + r - 1) = 2^{r-1} \cdot f(q^2) ,则:
证毕。
于是,可以简化以上表达式。创建数列
由
数列
代码如下:
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;
using LL = long long;
const LL MOD = 1'000'000'007;
LL sq(LL x)
{
return x * x;
}
LL isqrt(LL x)
{
return (LL)floor(sqrt(x));
}
LL pow_mod(LL base, LL exp)
{
LL result = 0, mult = 0;
result = 1;
mult = base;
while (exp != 0)
{
if (exp % 2 != 0)
result = result * mult % MOD;
mult = mult * mult % MOD;
exp /= 2;
}
return result;
}
struct SqrtCounter
{
vector<LL> c, ps;
SqrtCounter(LL x) : c(isqrt(x) + 1), ps(isqrt(x) + 1) {}
LL count(LL x)
{
if (x <= 3)
return 1;
return c[isqrt(x)] * pow_mod(2, x - sq(isqrt(x))) % MOD;
}
LL prefix_sum(LL x)
{
if (x == 2)
return 1;
if (x == 3)
return 2;
return (ps[isqrt(x)] +
c[isqrt(x)] * (pow_mod(2, x - sq(isqrt(x)) + 1) - 2 + MOD) % MOD) %
MOD;
}
};
int main()
{
LL x = 0, sqrt_x = 0;
cin >> x;
SqrtCounter counter(x);
counter.c[2] = 1;
counter.ps[2] = 3;
for (LL y = 3; y <= isqrt(x); ++y)
{
counter.c[y] = (counter.prefix_sum(sq(y) - 1) - counter.prefix_sum(y) + MOD) % MOD;
counter.ps[y] = (counter.prefix_sum(sq(y) - 1) + counter.c[y]) % MOD;
}
cout << counter.count(x) << '\n';
}