P4619 [SDOI2018]旧试题
题目描述
求下面表达式的值
[largesum_{i=1}^{A}sum_{j=1}^{B}sum_{k=1}^{C}sigma_{0}(ijk) mod(10^9+7)
]
数据范围
(1 le A,B,C le 100000)
解题思路
这题非常困难是因为推着推着式子变成了图论问题?
让我们先来推一波式子把
要用到的结论
[large sigma_{0}(ijk)=sum_{x|i}sum_{y|j}sum_{z|k}[gcd(x,y)=1][gcd(y,z)=1][gcd(x,z)=1]\
[x=1] = sum_{d|x}mu(d)
]
第一个式子的理解
单独考虑一个质因子 P,i 中最大有 (P^a),j 中最大有 (P^b),k 中最大有 (P^c),那么 (ijk) 中有 (P^{a+b+c}), 现在我们尝试建立起映射,选因数时,如果 P 选了小于等于 a 个,那么全在 i 中选,显然满足全部互质的条件,大于 a 个但小于等于 a + b 个,那么优先在 i 中选,剩下的在 j 中选,然后将 i 清零,大于 a + b 也同理,也就是选质数时尽量靠前选,但我们只让最后一个被选到的有,前面的设为 0,这样可以保证互质,且是一种双射关系
第二个式子的理解
裸的莫比乌斯反演
正式开始推式子
[largesum_{i=1}^{A}sum_{j=1}^{B}sum_{k=1}^{C}sigma_{0}(ijk)\
large=sum_{i=1}^{A}sum_{j=1}^{B}sum_{k=1}^{C}sum_{x|i}sum_{y|j}sum_{z|k}[gcd(x,y)=1][gcd(y,z)=1][gcd(x,z)=1]\
large=sum_{x=1}^{A}sum_{y=1}^{B}sum_{z=1}^{C}[gcd(x,y)=1][gcd(y,z)=1][gcd(x,z)=1]sum_{i=1}^{frac Ax}sum_{j=1}^{frac By}sum_{k=1}^{frac Cz}\
large=sum_{x=1}^{A}sum_{y=1}^{B}sum_{z=1}^{C}[gcd(x,y)=1][gcd(y,z)=1][gcd(x,z)=1]lfloorfrac Ax
floorlfloorfrac By
floorlfloorfrac Cz
floor\
large=sum_{x=1}^{A}sum_{y=1}^{B}sum_{z=1}^{C}sum_{u|x,u|y}mu(u)sum_{v|y,v|z}mu(v)sum_{w|x,w|z}mu(w)lfloorfrac Ax
floorlfloorfrac By
floorlfloorfrac Cz
floor\
large=sum_{u=1}^{min(A,B)}sum_{v=1}^{min(B,C)}sum_{w=1}^{min(A,C)}mu(u)mu(v)mu(w)sum_{x=lcm(u,w)}lfloorfrac Ax
floorsum_{y=lcm(u,v)}lfloorfrac By
floorsum_{z=lcm(v,w)} lfloorfrac Cz
floor\
large=sum_{u=1}^{min(A,B)}sum_{v=1}^{min(B,C)}sum_{w=1}^{min(A,C)}mu(u)mu(v)mu(w)F(lcm(u,w),A)F(lcm(u,v),B)F(lcm(v,w),C)
]
然后就是超级神奇的操作
我们把枚举的三元组抽象为一个三元环 (x, y, z),两个点间连边权为 lcm 的边,这个三元环对答案有贡献当且仅当
- (mu(x) eq0 ~~mu(y) eq0~mu(z) eq0)
- (lcm(x, y, z) le max(A, B, C))
你觉得它可能会快,但你认为它还是暴力,事实上它跑的飞快(比纯暴力来说
枚举三元环肯定不是暴力枚举,容易发现 lcm 的 (mu) 也不为零,我们可以直接枚举 lcm,将其质因数分解,根据质因数枚举其中一个端点,然后暴力枚举另一个端点,这样枚举的每一个都是合法的点对,时间复杂度 (Theta(m))
打表发现有大概 70 多万条边,暴力枚举肯定不行,用上黑科技 三元环计数 可以做到 (Theta(msqrt m)),卡卡常勉强跑过。
一发过了,但除法真的比乘法慢一大堆,请谨慎使用除法!!
#pragma GCC optimize(3, "inline")
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template<typename F>
inline void write(F x)
{
static short st[30];short tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
putchar('
');
}
template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }
template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }
#define re register
const int Maxn = 300000, N = Maxn + 5;
const int P = 1e9+7;
ll f[N], mu[N], e[N], prime[N], p[N], tot;
void prework(void) {
mu[1] = 1; f[1] = 1;
for (re int i = 2;i <= Maxn; i++) {
if (!e[i]) prime[++tot] = e[i] = i, mu[i] = -1, f[i] = p[i] = 2;
for (re int j = 1; prime[j] * i <= Maxn && j <= tot; j++) {
int t = prime[j] * i; e[t] = prime[j];
if (prime[j] == e[i]) { p[t] = p[i] + 1, f[t] = f[i] / p[i] * p[t]; break; }
f[t] = f[i] * (p[t] = 2), mu[t] = mu[i] * -1;
}
// cout << f[i] << endl;
f[i] = f[i] + f[i-1]; if (f[i] >= P) f[i] -= P;
}
}
struct node {
int x, y, lcm;
}ed[N*20];
ll ans;
ll vis[N], lm[N], a[N], b[N], c[N], A, B, C;
ll st[N], deg[N], lim, tp, T;
vector<pair<ll, ll> > v[N];
inline void clear(void) {
lim = max(max(A, B), C), tot = ans = 0;
for (int i = 1;i <= lim; i++)
a[i] = f[A/i], b[i] = f[B/i], c[i] = f[C/i], v[i].clear();
}
inline ll calc(int x, int y, int z) {
return a[x] * b[y] * c[z];
}
inline void build(void) {
// cout << calc(2, 1, 2) + calc(2, 2, 1) + calc(1, 2, 2) << endl;
memset(deg, 0, lim * 4 + 200);
for (int i = 1;i <= lim; i++) {
if (!mu[i]) continue; int x = i; tp = 0;
// if (i % 1000 == 0) cout << i << endl;
while (x != 1) st[++tp] = e[x], x /= e[x];
for (re int j = 0; j < (1 << tp); j++) {
int u = 1, t, v;
for (re int k = 1;k <= tp; k++)
if (j & (1 << (k - 1))) u *= st[k];
t = i / u;
for (int s = j; ; s = (s - 1) & j) {
v = t;
for (int p = 0;p < tp; p++)
if (s >> p & 1) v *= st[p + 1];
// cout << u << ' ' << v << endl;
if (u > v) {
ed[++tot] = (node) {u, v, i}; deg[u]++, deg[v]++;
// cout << u << ' ' << i << endl;
// cout << calc(u, i, i) + calc(i, u, i) + calc(i, i, u) << endl;
ans += mu[v] * (calc(u, i, i) + calc(i, u, i) + calc(i, i, u));
ans += mu[u] * (calc(v, i, i) + calc(i, v, i) + calc(i, i, v));
}
if (!s) break;
}
}
ans += mu[i] * calc(i, i, i);
}
for (int i = 1;i <= tot; i++) {
int x = ed[i].x, y = ed[i].y;
if (deg[x] > deg[y] || (deg[x] == deg[y] && x > y)) swap(x, y);
v[x].emplace_back(y, ed[i].lcm);
}
}
void work(void) {
memset(vis, 0, lim * 4 + 200);
// cout << calc(10, 5, 2) + calc(10, 2, 5) + calc(2, 5, 10) + calc(2, 10, 5) << endl;
for (int i = 1;i <= lim; i++) {
for (pair<int, int> to: v[i])
vis[to.fi] = i, lm[to.fi] = to.se;
for (pair<int, int> to: v[i]) {
for (pair<int, int> To: v[to.fi]) {
if (vis[To.fi] != i) continue;
// int pre = ans;
// cout << lm[To.fi] << ' ' << to.se << ' ' << To.se << endl;
ans += mu[i] * mu[to.fi] * mu[To.fi] *
( calc(lm[To.fi], to.se, To.se)
+ calc(lm[To.fi], To.se, to.se)
+ calc(to.se, lm[To.fi], To.se)
+ calc(to.se, To.se, lm[To.fi])
+ calc(To.se, lm[To.fi], to.se)
+ calc(To.se, to.se, lm[To.fi]) );
// cout << ans - pre << endl;
}
}
ans %= P;
}
ans = (ans % P + P) % P;
}
int main() {
for (prework(), read(T); T; T--) {
read(A), read(B), read(C);
clear(), build(), work(), write(ans);
}
return 0;
}
/*
1
100000 100000 100000
*/