P4619 [SDOI2018]旧试题
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4619 [SDOI2018]旧试题相关的知识,希望对你有一定的参考价值。
题意:
求个式子:
(
∑
i
=
1
A
∑
j
=
1
B
∑
k
=
1
C
d
(
i
∗
j
∗
k
)
)
m
o
d
(
1
0
9
+
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;
}
以上是关于P4619 [SDOI2018]旧试题的主要内容,如果未能解决你的问题,请参考以下文章
P4619 [SDOI2018]旧试题(莫比乌斯反演,建图优化三重枚举,三元环计数,神仙好题,超级清晰易懂)