[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 ;
}
posted @ 2019-04-10 07:33  beretty  阅读(314)  评论(0编辑  收藏  举报