hdu5072 Coprime

36 篇文章 0 订阅

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.

可以把原问题分解为两个子问题,一是求数列中与某个数互质的数的个数,二是单色三角形问题。
对于子问题一,先扫一遍对每个数进行质因数分解,枚举因数统计每个数作为数列中的一个数的因数出现的次数,再扫一遍枚举因数用容斥原理计算。
对于子问题二,如果对于a[i]有x个数与他互质,可以构成x * (n-x+1)个不满足要求的三元组。对所有a[i]的上述值求和,每个三元组被统计两遍。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define LL long long
int cnt[100010],a[100010],tem[100010][10],num[100010],
n;
int main()
{
    int T,i,j,k,m,x,u,y;
    LL ans;
    scanf("%d",&T);
    while (T--)
    {
        memset(cnt,0,sizeof(cnt));
        scanf("%d",&n);
        ans=0;
        for (i=1;i<=n;i++)
          scanf("%d",&a[i]);
        for (i=1;i<=n;i++)
        {
            m=sqrt(a[i]+0.5);
            num[i]=0;
            for (j=2;j<=m;j++)
              if (a[i]%j==0)
              {
                tem[i][num[i]++]=j;
                while (a[i]%j==0) a[i]/=j;
              }
            if (a[i]>1) tem[i][num[i]++]=a[i];
            for (j=1;j<(1<<num[i]);j++)
            {
                x=1;
                for (k=0;k<num[i];k++)
                  if (1<<k&j)
                    x*=tem[i][k];
                cnt[x]++;
            }
        }
        for (i=1;i<=n;i++)
        {
            u=0;
            for (j=1;j<(1<<num[i]);j++)
            {
                x=1;
                y=0;
                for (k=0;k<num[i];k++)
                  if (1<<k&j)
                    x*=tem[i][k],y++;
                if (y&1) u+=cnt[x]-1;
                else u-=cnt[x]-1;
            }
            ans+=(LL)u*(n-u-1);
        }
        printf("%lld\n",(LL)n*(n-1)*(n-2)/6-ans/2);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值