[SDOI2018]旧试题
题解
先扯一点儿前置知识:
三元环计数
就是先求出原无向图中每个点的度数
然后在重新连边的时候连有向边,要保证连的边是度数小的连向度数大的(度数大的连度数小的也行,就是要保持一致),度数相同标号小的往标号大的连边
然后计数就是先枚举一个点\(u\)的所有出边,然后把与这个点所有相邻的点都打上标记
然后再枚举这个点的所有出边的相邻的点\(v\),再枚举与\(v\)相邻的点\(w\),如果\(w\)被标记了就说明是一个三元环
这玩意儿复杂度是\(O(m\sqrt m)\)的
大致代码就长这样:
for(int u = 1 ; u <= n ; u ++) {
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ;
vis[v] = u ;
}
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ;
for(int j = hea[v] ; j ; j = edge[j].nxt) {
int w = edge[j].to ;
if(vis[w] == u)
++ ans ;
}
}
}
然后再说这道题
题目让求\(\sum_{i=1}^{A}\sum_{j=1}^{B}\sum_{k=1}^{C}{d(ijk)}\;\;
d\)表示约数个数和
先简单化简一下式子
\(\sum_{i=1}^{A}\sum_{j=1}^{B}\sum_{k=1}^{C}{d(ijk)}\\
=\sum_{i=1}^{A}\sum_{j=1}^{B}\sum_{k=1}^{C}\sum_{a|i}\sum_{b|j}\sum_{c|k}[gcd(a,b)=1][gcd(a,c)=1][gcd(b,c)=1]\\
=\sum_{a=1}^{A}\sum_{b=1}^{A}\sum_{c=1}^{C}[gcd(a,b)=1][gcd(a,c)=1][gcd(b,c)=1]\frac{A}{a}\frac{B}{b}\frac{C}{c}\\
=\sum_{a=1}^{A}\sum_{b=1}^{A}\sum_{c=1}^{C}\sum_{d|a\&d|b}{\mu(d)}\sum_{e|a\&e|c}{\mu(e)}\sum_{f|b\&f|c}{\mu(f)}\frac{A}{a}\frac{B}{b}\frac{C}{c}\)
然后我们还是交换一发求和号
可以发现交换这一发求和号以后\(a,b,c\)的限制就是\(d|a\& e|a , d|b \& f|b , e|c\& f|c\)
这样以后就变成了
\(\sum_{d=1}^{min(A,B)}\sum_{e=1}^{min(A,C)}\sum_{f=1}^{min(B,C)}\mu(d)\mu(e)\mu(f)\sum_{d|a\& e|a}\sum_{d|b \& f|b}\sum_{e|c\& f|c}\frac{A}{a}\frac{B}{b}\frac{C}{c}\\
\sum_{d=1}^{min(A,B)}\sum_{e=1}^{min(A,C)}\sum_{f=1}^{min(B,C)}\mu(d)\mu(e)\mu(f)\sum_{lcm(d,e)|a}\sum_{lcm(d,f)|b}\sum_{lcm(e,f)|c}\frac{A}{a}\frac{B}{b}\frac{C}{c}\)
首先可以在\(O(nlogn)\)的时间复杂度内求出\(fA,fB,fC,fA(x)=\sum_{x|d}\frac{A}{d},fB,fC\)同理。
那么我们就把枚举的\(d,e,f\)每个数都当做一个点
暴力的想法就是枚举两个值然后连边,然后对每个三元环来计数
我们发现暴力枚举两个值太慢了
可以枚举一个值\(i\),然后枚举一个\(gcd \ k\),再去枚举另外一个值\(j\),如果\(i,j\)互质,那么就得到了两个点\(i\times k , j\times k,lcm\)为\(ijk\)
然后连\((ik , jk , ijk)\)边即可
可以发现这玩意可以大力剪枝,首先要满足\(\mu\)必须不是0才有效
并且\(ijk\)也不能大于\(max(A,B,C)\)
这样一减下来复杂度就比较优秀了
我们对三元环计数记的时候一个环只会被计算一次
但是在一个环中就应该计算\(6\)次,所以在环中暴力计算\(6\)次
另外由于枚举的\(d,e,f\)是可以相等的,所以还要考虑所有的二元环和自环
代码
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int M = 200005 ;
const int mod = 1e9 + 7 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x * w ;
}
bool notp[M] ;
int n , A , B , C ;
LL ans , fA[M] , fB[M] , fC[M] ;
int pnum , p[M] , mu[M] , vis[M] , d[M] ;
struct E { int v , w ; } ;
vector < E > vec[M] ;
inline void Clear() {
ans = 0 ;
memset(vis , 0 , sizeof(vis)) ;
memset(fA , 0 , sizeof(fA)) ;
memset(fB , 0 , sizeof(fB)) ;
memset(fC , 0 , sizeof(fC)) ;
memset(d , 0 , sizeof(d)) ;
for(int i = 1 ; i <= n ; i ++) vec[i].clear() ;
}
inline void add_edge(int u , int v , int w) {
vec[u].push_back((E) { v , w } ) ;
}
int gcd(int a , int b) {
if(b == 0) return a ;
return gcd(b , a % b) ;
}
inline int lcm(int a , int b) {
return a / gcd(a , b) * b ;
}
inline void Pre(int n) {
mu[1] = 1 ;
for(int i = 2 ; i <= n ; i ++) {
if(!notp[i]) {
p[++pnum] = i ;
mu[i] = -1 ;
}
for(int j = 1 ; j <= pnum && i * p[j] <= n ; j ++) {
notp[i * p[j]] = true ;
if(i % p[j] == 0) {
mu[i * p[j]] = 0 ;
break ;
}
else mu[i * p[j]] = -mu[i] ;
}
}
}
int main() {
int Case = read() ;
Pre(200000) ;
while(Case --) {
A = read() ; B = read() ; C = read() ;
n = max( max( A , B ) , C ) ;
for(int i = 1 ; i <= n ; i ++) {
for(int j = i ; j <= n ; j += i)
fA[i] += A / j , fB[i] += B / j , fC[i] += C / j ;
fA[i] %= mod ; fB[i] %= mod ; fC[i] %= mod ;
}
for(int i = 1 ; i <= n ; i ++)
ans += mu[i] * fA[i] * fB[i] * fC[i] ;
for(int i = 1 , u , v , w ; i <= n ; i ++) {
if(!mu[i]) continue ;
for(int k = 1 ; k <= n && i * k <= n ; k ++) {
if(!mu[i * k]) continue ;
for(int j = 1 ; j <= n && 1LL * i * j * k <= n ; j ++) {
if(i == j) continue ;
if(!mu[j * k]) continue ;
if(gcd(i , j) > 1) continue ;
u = i * k , v = j * k , w = i * j * k ;
if(v > u) break ;
if(d[u] > d[v]) swap(u , v) ;
++ d[u] ; ++ d[v] ;
}
}
}
for(int i = 1 , u , v , w ; i <= n ; i ++) {
if(!mu[i]) continue ;
for(int k = 1 ; k <= n && i * k <= n ; k ++) {
if(!mu[i * k]) continue ;
for(int j = 1 ; j <= n && 1LL * i * j * k <= n ; j ++) {
if(i == j) continue ;
if(!mu[j * k]) continue ;
if(gcd(i , j) > 1) continue ;
u = i * k , v = j * k , w = i * j * k ;
ans += 1LL * mu[u] * mu[u] * mu[v] * fA[u] * fB[w] * fC[w] ;
ans += 1LL * mu[u] * mu[u] * mu[v] * fB[u] * fA[w] * fC[w] ;
ans += 1LL * mu[u] * mu[u] * mu[v] * fC[u] * fA[w] * fB[w] ;
if(u > v) continue ;
if(d[u] > d[v]) swap(u , v) ;
add_edge(u , v , w) ;
}
}
}
for(int a = 1 ; a <= n ; a ++) {
for(int i = 0 , b , fsz = vec[a].size() ; i < fsz ; i ++ ){
b = vec[a][i].v ;
vis[b] = a ;
}
for(int i = 0 , b , fsz = vec[a].size() , w1 ; i < fsz ; i ++ ){
b = vec[a][i].v ; w1 = vec[a][i].w ;
for(int j = 0 , c , ssz = vec[b].size() , w2 , w3 ; j < ssz ; j ++) {
c = vec[b][j].v ; w2 = vec[b][j].w ;
if(vis[c] == a) {
w3 = lcm( a , c ) ;
ans += mu[a] * mu[b] * mu[c] * fA[w1] * fB[w2] * fC[w3] ;
ans += mu[a] * mu[b] * mu[c] * fA[w1] * fB[w3] * fC[w2] ;
ans += mu[a] * mu[b] * mu[c] * fA[w2] * fB[w1] * fC[w3] ;
ans += mu[a] * mu[b] * mu[c] * fA[w2] * fB[w3] * fC[w1] ;
ans += mu[a] * mu[b] * mu[c] * fA[w3] * fB[w1] * fC[w2] ;
ans += mu[a] * mu[b] * mu[c] * fA[w3] * fB[w2] * fC[w1] ;
}
}
}
}
ans = (ans % mod + mod) % mod ;
printf("%lld\n",ans % mod) ;
Clear() ;
}
return 0 ;
}