LibreOJ 2565.「SDOI2018」旧试题

拿这个代码交了 JZOJ 4991(此题的 A,B,C5000A,B,C \le 5000 弱化版),成功拿到 14ms 最优解(

,类似地有 f1,f2f_1,f_2,上式变为

i=1min(A,B)μ(i)j=1min(A,C)μ(j)k=1min(B,C)μ(k)f0(lcm(i,j))f1(lcm(i,k))f2(lcm(j,k))\sum\limits_{i=1}^{\min(A,B)}\mu(i) \sum\limits_{j=1}^{\min(A,C)}\mu(j) \sum\limits_{k=1}^{\min(B,C)}\mu(k) f_0(\text{lcm}(i,j)) f_1(\text{lcm}(i,k)) f_2(\text{lcm}(j,k))

注意到可以把枚举的三元组 (i,j,k)(i,j,k) 看做一堆三元环然后做三元环计数,同时统计答案。
注意到有很多无贡献的边可以直接删掉,包括 μ(u)=0,μ(v)=0,lcm(u,v)>min(A,B,C)\mu(u) = 0,\mu(v) = 0,\text{lcm}(u,v) > \min(A,B,C)u,vu,v 的边。
于是我们剪枝剪掉一堆边之后边就变得很少了,直接暴力上三元环计数。

同时注意三元环计数无法考虑自环,特判一下有两个数相等或是三个数均相等的三元组即可。
据说 Cache Miss 使得 vector 存图会快很多?
参考了 GKxx 大佬的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1e5;
const long long mod = 1e9 + 7;
int T,A,B,C,mx,mn;
int vis[N + 5],cnt,prime[N + 5],mu[N + 5];
long long f[3][N + 5],ans;
int deg[N + 5];
vector<int> e[N + 5];
vector<long long> v[N + 5];
struct Edge
{
int u,v,w;
} g[(N << 4) + 5];
int tot;
int main()
{
mu[1] = 1;
for(register int i = 2;i <= N;++i)
{
if(!vis[i])
mu[prime[++cnt] = i] = -1;
for(register int j = 1;j <= cnt && i * prime[j] <= N;++j)
{
vis[i * prime[j]] = 1;
if(!(i % prime[j]))
break;
else
mu[i * prime[j]] = -mu[i];
}
}
memset(vis,0,sizeof vis);
scanf("%d",&T);
for(;T;--T)
{
scanf("%d%d%d",&A,&B,&C),mx = max(A,max(B,C)),mn = min(A,min(B,C)),ans = 0,memset(f,0,sizeof f);
for(register int i = 1;i <= A;++i)
for(register int j = i;j <= A;j += i)
f[0][i] += A / j;
for(register int i = 1;i <= B;++i)
for(register int j = i;j <= B;j += i)
f[1][i] += B / j;
for(register int i = 1;i <= C;++i)
for(register int j = i;j <= C;j += i)
f[2][i] += C / j;
for(register int i = 1;i <= mn;++i)
mu[i] && (ans += mu[i] * mu[i] * mu[i] * f[0][i] * f[1][i] * f[2][i]);
memset(deg,0,sizeof deg),tot = 0;
for(register int d = 1;d <= mx;++d)
for(register int i = 1;i * d <= mx;++i)
if(mu[i * d])
for(register int j = i + 1,x,y,lcm;(long long)i * j * d <= mx;++j)
if(mu[j * d] && __gcd(i,j) == 1)
x = i * d,y = j * d,lcm = i * j * d,
ans += mu[x] * mu[x] * mu[y] * (
f[0][x] * f[1][lcm] * f[2][lcm] +
f[0][lcm] * f[1][x] * f[2][lcm] +
f[0][lcm] * f[1][lcm] * f[2][x]),
ans += mu[x] * mu[y] * mu[y] * (
f[0][y] * f[1][lcm] * f[2][lcm] +
f[0][lcm] * f[1][y] * f[2][lcm] +
f[0][lcm] * f[1][lcm] * f[2][y]),
++deg[x],++deg[y],g[++tot] = (Edge){x,y,lcm};
for(register int i = 1;i <= mx;++i)
e[i].clear(),v[i].clear();
for(register int i = 1;i <= tot;++i)
if(deg[g[i].u] > deg[g[i].v] || (deg[g[i].u] == deg[g[i].v] && g[i].u < g[i].v))
e[g[i].u].push_back(g[i].v),v[g[i].u].push_back(g[i].w);
else
e[g[i].v].push_back(g[i].u),v[g[i].v].push_back(g[i].w);
for(register int x = 1;x <= mx;++x)
if(mu[x])
{
for(register int i = 0;i < e[x].size();++i)
vis[e[x][i]] = v[x][i];
for(register int i = 0,y,xy;i < e[x].size() && (y = e[x][i],xy = v[x][i],1);++i)
if(mu[y])
for(register int j = 0,z,xz,yz;j < e[y].size() && (z = e[y][j],xz = vis[z],yz = v[y][j],1);++j)
if(xz && mu[z])
ans += mu[x] * mu[y] * mu[z] * (
f[0][xy] * f[1][xz] * f[2][yz] +
f[0][xy] * f[1][yz] * f[2][xz] +
f[0][xz] * f[1][xy] * f[2][yz] +
f[0][xz] * f[1][yz] * f[2][xy] +
f[0][yz] * f[1][xy] * f[2][xz] +
f[0][yz] * f[1][xz] * f[2][xy]
);
for(register int i = 0;i < e[x].size();++i)
vis[e[x][i]] = 0;
}
printf("%lld\n",ans % mod);
}
}
arknights