zoukankan      html  css  js  c++  java
  • [SDOI2018]旧试题


    题解

    先扯一点儿前置知识:
    三元环计数
    就是先求出原无向图中每个点的度数
    然后在重新连边的时候连有向边,要保证连的边是度数小的连向度数大的(度数大的连度数小的也行,就是要保持一致),度数相同标号小的往标号大的连边
    然后计数就是先枚举一个点(u)的所有出边,然后把与这个点所有相邻的点都打上标记
    然后再枚举这个点的所有出边的相邻的点(v),再枚举与(v)相邻的点(w),如果(w)被标记了就说明是一个三元环
    这玩意儿复杂度是(O(msqrt 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 imes k , j imes 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
    ",ans % mod) ;
    		Clear() ;
    	}
    	return 0 ;
    }
    
  • 相关阅读:
    haproxy tcp 反向代理
    c# 字节高低位
    Mac Launchpad图标调整
    vsftp 777权限
    centos7下tomcat7 或tomcat8启动超慢原因
    mysql连接com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link
    Mac mysql 运行sql文件中文乱码的问题
    Mac Mysql [ERR] 2006
    记一次部署java项目的问题
    典型的响应式布局实例代码
  • 原文地址:https://www.cnblogs.com/beretty/p/10681052.html
Copyright © 2011-2022 走看看