P4619 [SDOI2018]旧试题
题意:
求个式子:
(∑i=1A∑j=1B∑k=1Cd(i∗j∗k))mod(109+7)(\sum_{i=1}^{A}\sum_{j=1}^{B}\sum_{k=1}^{C}d(i*j*k))mod(10^9+7)(i=1∑Aj=1∑Bk=1∑Cd(i∗j∗k))mod(109+7)
题解:
原创博文1k纪念
很明显,莫比乌斯反演
马上更新
三元环链接
此等好题,作为第1000题的纪念
代码:
这个题让我写,我是真写不出来,非常适合练练莫比乌斯推导
代码条例非常清晰,很适合阅读
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N= 1e5 + 7;
const int M= 1e6 + 7;
const int mod= 1e9 + 7;
bool Prime[N];
int tot, P[N >> 3], mu[N];
void get_mu()
{mu[1]= 1;for (int i= 2; i <= 1e5; i++) {if (!Prime[i])P[++tot]= i, mu[i]= -1;for (int j= 1; P[j] * i <= 1e5 && j <= tot; j++) {Prime[P[j] * i]= 1;if (i % P[j] == 0) {mu[P[j] * i]= 0;break;}mu[P[j] * i]= -mu[i];}}
}
vector<int> ve[N];
unordered_map<int, bool> mp[N];
int T, Max, A, B, C, ans, p[N];
int sA[M], sB[M], sC[M], cnt, deg[N];
int gcd(int x, int y)
{return x == 0 ? y : gcd(y % x, x);
}
ll lcm(int x, int y)
{return 1ll * x / gcd(x, y) * y;
}
int ta, tb, tc, sum;
void cal(int a, int b, int c)//求后半程三个累加的答案
{sum=(sum+ 1ll * p[a / ta] * p[b / tb] % mod * p[c / tc] % mod)% mod;
}
void get(int a, int b, int c)
{sum= 0;if (a == b && b == c)cal(A, B, C);else if (a == b || b == c || c == a) {cal(A, B, C);cal(C, A, B);cal(B, C, A);}else {//三个点的值都不一样,完全排列 cal(A, B, C);cal(A, C, B);cal(B, A, C);cal(B, C, A);cal(C, A, B);cal(C, B, A);}//记录后半程的答案 ans= (ans + (mu[a] * mu[b] * mu[c] * sum % mod + mod) % mod) % mod;//记录前半程答案
}
int vis[N];
void solve()
{for (int i= 1; i <= cnt; i++) { int &u= sA[i], &v= sB[i];if (deg[u] < deg[v])swap(u, v);else if (deg[u] == deg[v] && u > v)swap(u, v);ve[u].push_back(i);//存的点的编号,为了方便后面取值 }for (int i= 1; i <= Max; i++) {//跑三元环 for (int j : ve[i])vis[sB[j]]= sC[j];for (int j : ve[i])for (int k : ve[sB[j]])if (vis[sB[k]]) {ta= vis[sB[k]];//分别是三个lcm tb= sC[j];tc= sC[k];get(i, sB[j], sB[k]);//三个点的值 }for (int j : ve[i])vis[sB[j]]= 0;}
}
void getans(int x)
{for (int l= 1, r; l <= x; l= r + 1) {r= (x / (x / l));p[x]= (p[x] + 1ll * (r - l + 1) * (x / l) % mod) % mod;//整除分块前缀和 }
}
int main()
{scanf("%d", &T);get_mu();for (int i= 1; i <= 1e5; i++)getans(i);while (T--) {scanf("%d%d%d", &A, &B, &C);ans= cnt= 0;Max= max(max(A, B), C);for (int i= Max; i >= 1; i--) {//i枚举的是三数的人最大公约数for (int j= i; j <= Max; j+= i) {if (!mu[j])continue;for (int k= j; 1ll*k*j /i<= 1ll * Max; k+= i) {//k*j/i就是这个两个数的lcm,要小于等于Max if (!mu[k])continue;if (!mp[j][k]) {
// printf("%d %d %d i=%d\n", j, k, j / i * k,i);++cnt;sA[cnt]= j;sB[cnt]= k;sC[cnt]= 1ll*j*k/ i ;//sC是j和k的最小公倍数 deg[j]++;deg[k]++;mp[j][k]= 1;}}}}solve();for (int i= 1; i <= Max; i++) {ve[i].clear();mp[i].clear();deg[i]= 0;}printf("%d\n", (ans + mod) % mod);}return 0;
}