您的位置:首页 > 运维架构

hdu 5072 Coprime(单色三角形问题+容斥原理)

2015-03-23 22:35 507 查看

Coprime

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)

Total Submission(s): 1072 Accepted Submission(s): 429



Problem Description
There are n people standing in a line. Each of them has a unique id number.

Now the Ragnarok is coming. We should choose 3 people to defend the evil. As a group, the 3 people should be able to communicate. They are able to communicate if and only if their id numbers are pairwise coprime or pairwise not coprime. In other words, if their
id numbers are a, b, c, then they can communicate if and only if [(a, b) = (b, c) = (a, c) = 1] or [(a, b) ≠ 1 and (a, c) ≠ 1 and (b, c) ≠ 1], where (x, y) denotes the greatest common divisor of x and y.

We want to know how many 3-people-groups can be chosen from the n people.


Input
The first line contains an integer T (T ≤ 5), denoting the number of the test cases.

For each test case, the first line contains an integer n(3 ≤ n ≤ 105), denoting the number of people. The next line contains n distinct integers a1, a2, . . . , an(1 ≤ ai ≤ 105) separated by
a single space, where ai stands for the id number of the i-th person.


Output
For each test case, output the answer in a line.


Sample Input
1
5
1 3 9 10 2




Sample Output
4




Source
2014 Asia AnShan Regional Contest

题目大意:给定一些数,用这些数构建三个数的集合,要求其中两两互质,或者两两不互质

题目分析:单色三角形模型,用补集的思想去想,也就是如果以某一个点开始找如果两条边异色,那么一定不是单色三角形,所以枚举与每个点相连的边里两种颜色的边的个数,然后计算n(红)*n(黄),因为是双向边,所以每条边会被算两次,所以要去重,也就是/2,那么对应到这道题就是去找补集,然后红边对应到互质的两个数,黄边对应不互质的两个数的关系,然后枚举每个点(值为1的点不枚举,因为它和任何数互质,也就是它只有一种颜色的边),确定点后找到与这个数互质的数的关系,再找到与它不互质的数的关系.直接找肯定超时,那么我们可以利用容斥定理做一个sqrt(10W)的优化,也就是与一个数互质的数和不互质的数相加起来一定是全集,然后我们只要求出当前确定的数的互质的数的数目即可,那么我们可以利用容斥定理进行,那么我们就可以枚举它的约数且这些约数,不包含两个及以上个相同的质因数,那么如果这些质因数的种类是奇数,那么我们减去它的倍数的个数,如果是偶数,我们加上他的倍数的个数,首先1会加上全体数,然后2因为是奇数个质因数,所以会减去所有2的倍数的数,3会减去所有三的倍数的数,但是因为既是3又是2的倍数的数就剪了两次,所以要加回来,......,那么就通了,而且发现一个很神奇的东西,也就是最后得出公式正好是

sum(约数的倍数的个数*约数对应的mu值),然胡这道题就可以ac了,其实用到的仅仅是容斥定理

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define MAX 100007

using namespace std;

typedef long long LL;

int t,n;
int mu[MAX];
int a[MAX];
int c[MAX];
int sum[MAX];
LL cop[MAX];
int prime[MAX];
int cnt;
int vis[MAX];

void init ( )
{
    memset ( vis , 0 , sizeof ( vis ) );
    mu[1] = 1;
    cnt = 0;
    for ( int i = 2 ; i < MAX ; i++ )
    {
        if ( !vis[i] )
        {
            prime[cnt++] = i;
            mu[i] = -1;
        }
        for ( int j = 0 ; j < cnt && i*prime[j] < MAX ; j++ )
        {
            vis[i*prime[j]] = 1;
            if ( i%prime[j] ) mu[i*prime[j]] = -mu[i];
            else 
            {
                mu[i*prime[j]] = 0;
                break;
            }
        } 
    }
}

int main ( )
{
    init ( );
    scanf ( "%d" , &t );
    while ( t-- )
    {
        scanf ( "%d" , &n );
        memset ( a , 0 , sizeof ( a ) );
        memset ( sum , 0 , sizeof ( sum ) );
        memset ( cop , 0 , sizeof ( cop ) );
        for ( int i = 1 ; i <= n ; i++ )
        {
            scanf ( "%d" , &c[i] );
            a[c[i]]++;
        }
        int lim = 100000;
        for ( int i = 1 ; i <= lim ; i++ )
           for ( int j = i ; j <= lim ; j += i )
              sum[i] += a[j];
        for ( int i = 1 ; i <= lim ; i++ )
           for ( int j = i ; j <= lim ; j += i )
             cop[j] += sum[i]*mu[i];
        LL ans = 0;
        for ( int i = 1 ; i <= n ; i++ )
            if ( c[i] != 1 )
                ans += (LL)(cop[c[i]])*(LL)(n-1-cop[c[i]]);
        ans = (LL)(n)*(LL)(n-1)*(LL)(n-2)/6 - ans/2;
        printf ( "%lld\n" , ans );
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: