Coprime HDU - 5072 2014 Asia AnShan Regional Contest B (数学 容斥)

题目链接 https://vjudge.net/problem/HDU-5072

题目大意

给定数组 a i a_i ai求满足以下两个条件其中一个的三元组 ( a , b , c ) (a,b,c) (a,b,c)的数量 p s : ( a , b , c ) = ( b , c , a ) ps:(a,b,c)=(b,c,a) ps:(a,b,c)=(b,c,a)
1. ( a , b ) = 1 , ( a , c ) = 1 , ( b , c ) = 1 (a,b)=1,(a,c)=1,(b,c)=1 (a,b)=1,(a,c)=1,(b,c)=1
2. ( a , b ) ≠ 1 , ( a , b ) ≠ 1 , ( b , c ) ≠ 1 (a,b)\not =1,(a,b)\not=1,(b,c)\not=1 (a,b)=1,(a,b)=1,(b,c)=1

解题思路

一开始没什么思路,题目的要求很繁琐,加上感觉像是容斥,就往容斥的一般思路,求满足反题目条件的数量,然后用总数量减,对于题目要求的两种情况,要考虑三个关系式,比较繁琐。
因为abc调换不影响计数,可以这样构建反问题:
( a , b ) = 1 , ( a , c ) ≠ 1 (a,b)=1,(a,c)\not =1 (a,b)=1,(a,c)=1这样不管bc之间的关系,都是题目的反问题。
但是这样必定会有两个二元组存在相同的状态 ( ( x , y ) ≠ 1 ∣ ∣ ( x , y ) = 1 ) ( (x,y)\not=1 ||(x,y)=1) ((x,y)=1(x,y)=1),在计算过程中如果枚举每一个数那就会算两次,最后问题的答案就是: ( n 3 ) − r e s 2 \binom{n}{3}-\frac {res}{2} (3n)2res

对于 a i a_i ai假如{ a n a_n an}中有 x x x个数和 a i a_i ai互质,那么和 a i a_i aigcd不为 1 1 1的数的数量就是 n − x − 1 n-x-1 nx1

接下来就是在{ a n a_n an}中求和{a_i}互质的数的数量了,这是一个经典的容斥模型:
可以转化成求:总数减去 和 a i {a_i} ai有公共因子的数

先对每个 a i a_i ai打一个 n u m [ i ] num[i] num[i]的表,表示有 n u m [ i ] num[i] num[i]个数有因子 i i i
然后对每个 a i {a_i} ai唯一分解,由于这里 a i < 1 e 5 a_i<1e5 ai<1e5这里的质因子的数量最多不超过 7 7 7个,然后容斥一下,奇减偶加

注意对 r e s res res产生贡献的时候不能出现负数

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
//#define int ll
#define debug cout<<"fuck"<<endl;
#define pb  push_back
#define endl '\n'
#define fi first
#define se second
#define db double
#define pii pair<int,int>
#define mp make_pair
const int mod=(int)1e9+7;
const int maxn=(int)1e5+5;
int n;
int a[maxn];
int cnt[maxn];
void init()
{
    memset(cnt,0,sizeof(cnt));

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j*j<=a[i];j++)
        {
            if(a[i]%j==0)
            {
                cnt[j]++;
                if(j!=a[i]/j)cnt[a[i]/j]++;
            }
        }
    }
}
vector<int>v;
int solve(int x)
{
    v.clear();
    int xx=x;
    for(int i=2;i*i<=xx;i++)
    {
        if(xx%i==0)
        {
            v.pb(i);
            while(xx%i==0)
            {
                xx/=i;
            }
        }
    }
    if(xx>1)v.pb(xx);

    int sz=(int)v.size();
    int ret=n;
    for(int i=1;i<(1<<sz);i++)
    {
        int cc=0;
        int now=1;
        for(int j=0;j<sz;j++)
        {
            if(i&(1<<j))
            {
                now*=v[j];
                cc++;
            }
        }
        if(cc&1)ret-=cnt[now];
        else ret+=cnt[now];
    }
    return ret;
}
int t;
int main()
{
    IOS
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        init();
        ll res=(ll)n*(n-1)*(n-2)/6;
        ll sum=0;
        for(int i=1;i<=n;i++)
        {
            int num1=solve(a[i]);
            //cout<<num1<<endl;
            int num2=max(0,n-num1-1);
            sum+=(ll)num1*num2;
        }
        cout<<res-sum/2<<endl;
    }





    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值