zoukankan      html  css  js  c++  java
  • LA 7048 Coprime 莫比乌斯反演

    题意:

    给出(n(n leq 10^5))个数字(a_i(a_i leq 10^5)),从中选出(3)个数,使得这(3)个数两两互质或者两两不互质

    分析:

    可以说这是《训练指南》(P_{105})问题(6)的原题。
    (n)个数看成(n)个顶点,如果两数互质连一条白边,不互质连一条黑边。
    那么我们要计数的就是单色三角形的个数。
    (n)个数中选(3)个数,一共有(C_n^3)种方案,正面不容易计算所以我们反面计算单色三角形的个数。
    在一个非单色三角形中,恰好有两个顶点连接两条异色边。
    而且有公共顶点的两条异色边对应一个非单色三角形。
    假设与(a_i)互质的数字的个数为(b_i)(相当于连了(b_i)条白边),那么与(a_i)不互质的数字的个数为(n-1-b_i)(连了(n-1-b_i)条黑边)
    每个非单色三角形被计算了两次,所以对应的个数为$ frac {1} {2} sum{b_i (n-1-b_i)}( 最后单色三角形的个数就是)C_n^3$减去非单色三角形的个数。

    关于计算与(a_i)互质的数字的个数,根据莫比乌斯反演公式有 $ sum{mu(d) cnt_d, (d | a_i)} (,其中)cnt_d(为)d$的倍数的个数。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #include <vector>
    using namespace std;
    
    typedef long long LL;
    const int maxn = 100000;
    
    int mu[maxn + 10], pcnt, prime[maxn];
    bool vis[maxn + 10];
    vector<int> factors[maxn + 10];
    
    void preprocess() {
    	pcnt = 0;
    	mu[1] = 1;
    	for(int i = 2; i <= maxn; i++) {
    		if(!vis[i]) {
    			mu[i] = -1;
    			prime[pcnt++] = i;
    		}
    		for(int j = 0; j < pcnt && i * prime[j] <= maxn; j++) {
    			vis[i * prime[j]] = true;
    			if(i % prime[j] != 0) mu[i * prime[j]] = -mu[i];
    			else {
    				mu[i * prime[j]] = 0;
    				break;
    			}
    		}
    	}
    
    	for(int i = 2; i <= maxn; i++) if(mu[i])
    		for(int j = i; j <= maxn; j += i) factors[j].push_back(i);
    }
    
    int n, a[maxn + 10], cnt[maxn + 10];
    
    int main()
    {
    	preprocess();
    	
    	int T; scanf("%d", &T);
    	while(T--) {
    		scanf("%d", &n);
    		
    		memset(cnt, 0, sizeof(cnt));
    		for(int i = 0; i < n; i++) {
    			scanf("%d", a + i);
    			for(int d : factors[a[i]]) cnt[d]++;
    		}
    
    		LL ans = 0;
    		for(int i = 0; i < n; i++) {
    			LL coprime = n;
    			for(int d : factors[a[i]]) coprime += mu[d] * cnt[d];
    			if(a[i] == 1) coprime--;
    			ans += coprime * (n - 1 - coprime);
    		}
    		ans >>= 1;
    		LL tot = (LL)n * (n-1) * (n-2) / 6;
    		printf("%lld
    ", tot - ans);
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    pwnable.kr login之write up
    安装ubuntu16.4后
    HTML资源定位器-URL
    【实验吧】编程循环&&求底运算
    【SQL注入】mysql中information_schema详解
    C#之BackgroundWorker从简单入门到深入精通的用法总结
    C#使用NPOI对Excel文档进行读、写、导入、导出等操作的dll最新版2.5.1+2.3.0
    Visual Studio中Debug与Release以及x86、x64、Any CPU的区别
    C# 使用BackgroundWorker例子及注意点
    C# BackgroundWorker组件学习入门介绍
  • 原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/4908097.html
Copyright © 2011-2022 走看看