Uva Coprime(莫比乌斯反演)

题意:n个数中每次选取三个数,这三个数两两互质或两两不互质,求满足条件的三个数的对数;

思路:http://blog.csdn.net/cool_fires/article/details/8681888(数据范围较小的题)

        考虑三个数互质的对数的情况可以分为0对,1对,2对,3对,题目要求0对和3对的情况,可以通过求1对和2对的情况取补;

        莫比乌斯反演用于辅助求不大于一个数的与该数互质的数的个数;

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int t,n,m;
bool check[100010];
int num[100010],cnt[100010],temp[100010];
int prime[100010],mu[100010],mul[100010];
void moblus(){
  memset(check,false,sizeof(check));
  mu[1]=1;
  int tot=0;
  for(int i=2;i<=100000;i++){
    if(!check[i]){
      prime[tot++]=i;
      mu[i]=-1;
    }
    for(int j=0;j<tot;j++){
      if(i*prime[j]>100000) break;
      check[i*prime[j]]=true;
      if(i%prime[j]==0){
          mu[i*prime[j]]=0;
          break;
      }
      else{
          mu[i*prime[j]]=-mu[i];
      }
    }
  }
}
int main(){
    int i,j,k;
    moblus();
    scanf("%d",&t);
    while(t--){
      scanf("%d",&n);
      memset(cnt,0,sizeof(cnt));
      memset(mul,0,sizeof(mul));
      memset(temp,0,sizeof(temp));
      for(i=1;i<=n;i++){
          scanf("%d",&num[i]);
          cnt[num[i]]++;
      }
      for(i=1;i<=100000;i++){
          for(j=i;j<=100000;j+=i){
            mul[i]+=cnt[j];    //i及其倍数的个数
          }
      }
      for(i=1;i<=100000;i++){
        for(j=i;j<=100000;j+=i){
           temp[j]+=mu[i]*mul[i];  //temp[j]为与j互质的数的个数
        }
      }
      long long ans;
      ans=(long long)n*(n-1)*(n-2)/6;
      long long res=0;
      for(i=1;i<=n;i++){
          if(num[i]==1) continue;
          res+=(long long)temp[num[i]]*(long long)(n-1-temp[num[i]]); //不包含自身,所以为n-1
      }
      ans-=res/2; //去掉重复计算的情况
      printf("%lld\n",ans);
    }
    return 0;
}

 

posted @ 2015-09-01 17:00  dominating大树置林  Views(210)  Comments(0)    收藏  举报