Coprime HDU - 5072 单色三角形+莫比乌斯反演+容斥原理

单色三角形:如果每个人都有关系的话,认识或者不认识,随便找6个人(或以上),则一定会有三个人互相认识,或者互相不认识。因为每个人和其他人都有关系嘛,认识或者不认识。则一个人A与其他5个人有都有关系,这个时候当A与其他5个人的关系中有两个认识,3个不认识(或者2个不认识,3个认识)的时候组成的、
红边表示认识,蓝边表示不认识,这个时候组成的两边相同的三角形最少。
这里写图片描述
通过画图我们知道以A为顶点的两边颜色相同的至少有三对(假设为红)。如果三角形另外一条边为红的话,就证明了有三个人互相认识。如果颜色相同的那三对另外一条边都是蓝色的话,这个时候这三条蓝边也组成了一个蓝色三角形/doge,得证。

单色三角形模型是:有两种边,红色的和蓝色的,问你有多少单色三角形(三角形的三条边全是同一种颜色)。正着数有多少个单色三角形,不好数。所以我们采用减法原理,将总共的三角形数目-非单色三角形。那么非单色三角形怎么计算呢?每一个非单色三角形中肯定是有两条边不一样的,也就是说有来两个顶点连接的是不一样的边。如果这个顶点连接的是不一样的边的话,也就是说这个点连了ai条蓝边,(n-1-ai)红边。一共是ai*(n-1-ai)种方案,然后累加起来sum,因为一条边连着两个点,所以最后结果是 sum/2,用c(n,3)-sum/2 就行了

这个题的意思是,让你求三个点要么两两互质,要么两两不互质的数量。(就是求单色三角形的个数嘛,这里红边蓝边就变成了互不互质了)

用单色三角形的思想,我们对一个点来说,连一个互质和一个不互质的,为ai*(n-1-ai),ai表示与这个互质的个数,然后c(n,3)-sum/2 就行了

那么问题就转变成了怎么求与这个点互质的个数呢?如果暴力的话,就是n^2,n会达到 1e5,超时,用莫比乌斯反演? 那个不是1-n之间的么。然后我们这里用的是容斥原理的思想。对于每个数-和他有相同单个公因子数+相同双因子数-……..

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <vector>

using namespace std;
const int maxn = 1e5+10;
#define LL long long
int a[maxn];
int vis[maxn];
int miu[maxn];
int prime[maxn];
void mobious()
{
    miu[1]=1;
    int tot=0;
    for(int i=2;i<=100000;i++)
    {
        if(!vis[i])
        {
            prime[tot++]=i;
            miu[i]=-1;
        }
        for(int j=0;j<tot;j++)
        {
            int k=i*prime[j];
            if(k>100000) break;
            vis[k]=1;
            if(i%prime[j]) miu[k]=-miu[i];
            else break;
        }
    }
}
int maxx;
LL n;
LL hz[maxn],num[maxn];
void solve()
{
    memset(num,0,sizeof(num));
    memset(hz,0,sizeof(hz));
    for(int i=1;i<=maxx;i++)
    {
        for(int j=i;j<=maxx;j+=i)//找是i个倍数的数量
             num[i]+=vis[j];
        for(int j=i;j<=maxx;j+=i)
            hz[j]+=miu[i]*num[i];
    }
    LL ans=0;
    for(int i=0;i<n;i++)
    {
        if(a[i]!=1)
        {
            ans+=(hz[a[i]]*(n-1-hz[a[i]]));
        }
    }
    ans=n*(n-1)*(n-2)/6-ans/2;
    printf("%I64d\n",ans);
}
int main()
{
    int t;
    scanf("%d",&t);
    mobious();
    while(t--)
    {
        memset(vis,0,sizeof(vis));
        memset(a,0,sizeof(a));
        scanf("%I64d",&n);
        maxx=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            vis[a[i]]++;
            maxx=max(maxx,a[i]);
        }
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值