洛谷P1829 [国家集训队] Crash的数字表格 / JZPTAB 题解
题目链接:P1829 [国家集训队] Crash的数字表格 / JZPTAB
题意:
给定 $n,m$ 求
模 $20101009$ 的结果。
数据范围:$1\le n,m \le 10^7$ 。
来讲一个杜教筛的解法吧。
首先先规定一下 $n \le m$ ,然后开始推式子
不妨设后面一堆东西为 $g\left(\left\lfloor\frac{n}{d}\right\rfloor,\left\lfloor\frac{m}{d}\right\rfloor\right)$ ,那么
那么代回原式可得
设 $f=(\mu\, \mathrm{id}^2) *\mathrm{id}$ ,也就是 $S$ 后面那个东西。
考虑构造一个 $g$ 以快速计算 $f$ 的前缀和。这里直接说了,就是 $g=\mathrm{id}^2$ ,那么
这里第一行利用了狄利克雷卷积的交换律。
其实到这里我们就可以在 $\mathcal{O}(n^{\frac{3}{4}})$ 的复杂度内求解出答案了
但是还能更快,前提是我们能够提前筛出前 $\mathcal{O}(n^{\frac{2}{3}})$ 个答案。
由于 $f(T) = \sum_{d \mid T}d\mu(d)$ 是一个积性函数,不妨考察为 $T$ 添加质因子 $p$ 得到 $Tp$ 时
- 若 $T$ 有质因子 $p$ ,乘以 $p$ 不会产生任何贡献,故此时 $f(Tp) = f(T)$ 。
- 若 $T$ 没有质因子 $p$ ,乘以 $p$ 会使 $\mu(Tp)=-\mu(T)$ ,故此时 $f(Tp)=f(T) -pf(T) = f(p)f(T)$
考虑线性筛筛出前 $\mathcal{O}(n^{\frac{2}{3}})$ 个答案即可。总时间复杂度为 $\mathcal{O}(n^{\frac{2}{3}})$
代码:(非常快!)
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
const int mod = 20101009, inv6 = 16750841;
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(5e6 + 15))
char ck[N]; int M, pcnt, p[N], f[N], sum[N];
unordered_map<int, int> vis;
void add(int &x, int y) { (x += y) >= mod ? x -= mod : 0; }
int qpow(int a, int b)
{
int r = 1;
for(a %= mod; b > 0; b >>= 1, a = a * a % mod)
if(b & 1) r = r * a % mod;
return r;
}
void init(const int _ = N - 5)
{
f[1] = 1;
rep(i, 2, _)
{
if(!ck[i]) { p[++pcnt] = i; f[i] = (1 + mod - i); }
for(int j = 1; j <= pcnt && i * p[j] <= _; j++)
{
const int pos = i * p[j]; ck[pos] = true;
if(i % p[j] == 0) { f[pos] = f[i]; break; }
else { f[pos] = f[i] * f[p[j]] % mod; }
}
}
rep(i, 1, _) add(sum[i] = sum[i - 1], f[i] * i % mod);
}
int fun1(int x) { x %= mod; return (x * (x + 1) / 2) % mod; }
int fun2(int x) { x %= mod; return x * (x + 1) % mod * (2 * x % mod + 1) % mod * inv6 % mod; }
int getsum(int n)
{
if(n <= M) return sum[n];
if(vis[n]) return vis[n];
int res = fun1(n);
for(int l = 2, r; l <= n; l = r + 1)
{
r = n / (n / l);
add(res, mod - (fun2(r) - fun2(l - 1) + mod) % mod * getsum(n / l) % mod);
}
return vis[n] = res;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int n, m; cin >> n >> m; init(M = min((int)powl(n, 0.67) + 10, N - 5));
if(n > m) swap(n, m); int res = 0;
for(int l = 1, r; l <= n; l = r + 1)
{
r = min(n / (n / l), m / (m / l));
add(res, (getsum(r) - getsum(l - 1) + mod) % mod * fun1(n / l) % mod * fun1(m / l) % mod);
}
cout << res << '\n';
return 0;
}
双倍经验:SP8099 TABLE - Crash´s number table
参考文献:
[1] https://www.luogu.com.cn/article/reipso0u
[2] https://blog.csdn.net/litble/article/details/79518721
题外话:
搞了个加强版 题目链接 (注意模数改了一下)