HDU5072Coprime 容斥原理+双色三角形 2014 Asia AnShan Regional Contest

本文解析了HDU 5072题目的解题思路,通过建立双色三角形计数模型,利用容斥原理计算每个数与其他数互素的数量,最终得出所有满足条件的数对个数。

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5072

题意:在1e5个数(<=1e5)中,找出有多少对(a,b,c) , 满足 ( gcd(a,b)==1 && gcd(a,c)==1 && gcd(b,c)==1 ) || ( gcd(a,b)!=1 && gcd(a,c)!=1 && gcd(b,c)!=1 ) .

这很像白书上一道双色三角形计数模型,即有n个点,一直每个点可连出x条红边和n-1-x条绿边,问有多少个双色多边形?    ans = n*(n-1)*(n-2)/6  - Sigma( a[i]*(n-1-a[i]) ) / 2 ;

那么这道题可以将互素看做红边,非互素看做绿边。现在我们只需找到每个数有多少和他互素即可。使用容斥定理。


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;

const int N = 1e5+10;
const int maxn = 100000;

vector<int> pri[N];
ll cnt[N];

void init(){
    for(int i=0;i<=maxn;i++)
        pri[i].clear();
    for(int i=2;i<=maxn;i++) if(pri[i].size()==0)
        for(int j=i;j<=maxn;j+=i)
            pri[j].push_back(i);
}

void add(int x,int y){
    int size = pri[x].size();
    for(int i=0;i<(1<<size);i++){
        int z = 1;
        for(int j=0;j<size;j++)
        if(i&(1<<j))
            z*=pri[x][j];
        cnt[z] += y;
    }
}

ll cal(int x){
    ll ans = 0;
    int size = pri[x].size();
    for(int i=0;i<(1<<size);i++){
        int z = 1;
        int fl = 1;
        for(int j=0;j<size;j++)
        if(i&(1<<j)){
            z*=pri[x][j];
            fl *= -1;
        }
        ans += cnt[z]*fl;
    }
    return ans;
}

int a[N];
ll b[N],c[N];
int main(){
    init();
    int T,n; cin >> T;
    while(T--){
        memset(cnt,0,sizeof(cnt));
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));

        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            add(a[i],1);
        }
        for(int i=1;i<=n;i++){
            add(a[i],-1);
            ll xx = cal(a[i]);
            b[i] = xx;
            c[i] = n-1 - xx;
            add(a[i],1);
        }
        ll ans = (ll)n*(ll)(n-1)*(ll)(n-2)/6;
        ll count = 0;
        for(int i=1;i<=n;i++){
            count += (ll)b[i]*(ll)c[i];
        }
        ans -= count/2 ;
        printf("%lld\n",ans);
    }
    return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值