标签:
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1509 Accepted Submission(s): 592
题目原形是同色三角形, 引用:就是求同色三角形的个数。总的三角形的个数是C(n,3),只需减去不同色的三角形即可。对于每个点(数),与它互质的连红边,不互质的连蓝边,那么对于该点不同色三角形个数为蓝边数*红边数/2,因为同一个三角形被计算了两次。那么同色三角形个数为C(n,3) - ∑蓝边数*红边数/2。
问题是:如何求 原来序列里面的n个数跟某个数k不互质的个数(互质的就是n-k了)?
可以将原来的n个数,每一个都把他们的不同的质因数都求出来,然后枚举它们能够组合的数(1 << cnt),用一个数组num记录,每枚举到一个数,那么数组对应的就+1
对于数k,也把它的不同质因数求出来,同样枚举它能够组合的所有数t,然后奇加偶减num
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <algorithm> #include <queue> #include <map> using namespace std; typedef long long ll; const int N = 200005; int p[N][15], vis[N], a[N], num[N]; int n; void Prime() { memset(vis, 0, sizeof vis); for(int i = 0; i < N; ++i) p[i][0] = 0; for(int i = 2; i < N; ++i) if(!vis[i]) { p[i][ ++p[i][0] ] = i; for(int j = i + i; j < N; j += i) { vis[j] = 1; p[j][ ++p[j][0] ] = i; } } p[0][ ++p[0][0] ] = 1; //考虑0的情况 } void init() { memset(num, 0, sizeof num); for(int k = 0; k < n; ++k) { int now = a[k]; int cnt = p[ now ][0]; for(int i = 1; i < (1 << cnt); ++i) { int t = 1; for(int j = 0; j < cnt; ++j) if((1 << j) & i) { t *= p[ now ][j + 1]; } num[t]++; } } } void solve() { ll ans = 0, res, sum = 0; ans = (ll)n * (n - 1) * (n - 2) / 6; //类型转换一下,避免爆掉 int tot = 0; for(int k = 0; k < n; ++k) { int now = a[k]; int cnt = p[now][0]; res = 0; for(int i = 1; i < (1 << cnt); ++i) { int t = 1, g = 0; for(int j = 0; j < cnt; ++j) if((1 << j) & i) { t *= p[ now ][j + 1]; g++; } if(g & 1) res += num[t]; else res -= num[t]; } if(res == 0) continue; sum += (res - 1) * (n - res); } printf("%lld\n", ans - sum / 2); } int main() { // freopen("in", "r", stdin); int _; scanf("%d", &_); Prime(); while(_ --) { scanf("%d", &n); for(int i = 0; i < n; ++i) scanf("%d", &a[i]); init(); solve(); } }
标签:
原文地址:http://www.cnblogs.com/orchidzjl/p/4762267.html