2014 鞍山区域赛 C HDU5072 Coprime 莫比乌斯/容斥+同色三角形

题意:n个数中,求三个数两两互质或者两两不互质的三元组的数量;

思路:单色三角形模型,也就是说完全图中,每两点间的边可以是黑色,可以是红色,求纯色三角形的数量;

直接求会非常困难,所以我们转化为对偶问题——求不纯色三角形的数量;

来关注某个三角形ABC,假设A固定,那么我们可以假设,A与B互质,A与C不互质,这时我们不用关心B与C之间是否互质,因为不影响结果;注意A不能是1,因为所有数都与1互质;

所以我们只需要对于每个数i,算出与之不互质的数ki,所以与之互质的数为n-ki-1个,又因为每个因此结果就是sigma(ki*(n-ki-1))/2,i=1…n;

于是题目就转换成了在某个集合中,快速求出与每个数不互质的数的个数,于是做法自然是莫比乌斯函数/容斥了;

先说比较好理解的容斥——对于每个数提取出因数,则比如求与30不互质的数,ki=num(2)+num(3)+num(5)-num(6)-num(10)-num(15)+num(30)-1;

容斥代码:

#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<cctype>

#include<iostream>
#include<vector>
#include<map>
#include<queue>
#include<string>
#include<set>
#include<algorithm>
#include<stack>
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define INF 0x7fffffff
#define ll long long
#define eps 1e-5
#define pii pair<int,int>
#define pll pair<ll,ll>
#define m_p make_pair
#define PI 2.0*acos(0.0)
#define MOD 1000000007

#define MAXN 1000

int numc[100*MAXN+5];
int factor[20], fac;
int f[100*MAXN+5];
ll sum;
int prime[MAXN], isprime[MAXN], num;


void getprimes()
{
    num = 0;
    //mem(prime, 0);
    mem(isprime, 1);
    for (int i = 2; i < MAXN; i++){
        if (isprime[i])
            prime[num++] = i;
        for (int j = 1; j<num && i*prime[j] <= MAXN; j++){
            isprime[i*prime[j]] = 0;
            if (i%prime[j] == 0) break;
        }
    }
}

void dfs1(int now, int s)
{
    if (now == fac)
    {
        numc[s]++;
        return;
    }
    dfs1(now + 1, s);
    dfs1(now + 1, s*factor[now]);
}

void dfs2(int id, int all, int now, int s)
{
    if (now == all)
    {
        sum += numc[s];
        return;
    }
    if (id < fac)
    {
        dfs2(id + 1, all, now + 1, s*factor[id]);
        dfs2(id + 1, all, now, s);
    }
}

void getfactors(int n)
{
    fac = 0;
    for (int i = 0; i < num&&prime[i] <= n; i++)
    {
        if (n%prime[i] == 0)
        {
            factor[fac++] = prime[i];
            while (n%prime[i] == 0) n /= prime[i];
        }
    }
    if (n > 1) factor[fac++] = n;
}

int main()
{
    getprimes();
    int T;
    cin >> T;
    while (T--)
    {
        int n;
        cin >> n;
        mem(numc, 0);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &f[i]);
            getfactors(f[i]);
            dfs1(0, 1);
        }
        ll ans = 0;
        for (int i = 1; i <= n; i++)
        {
            getfactors(f[i]);
            ll ret = 1;
            ll tmp = 0;
            for (int j = 1; j <= fac; j++)
            {
                sum = 0;
                dfs2(0, j, 0, 1);
                tmp += ret*sum;
                ret = -ret;
            }
            if (tmp == 0)
            {
                continue;
            }
            ans += (tmp - 1)*(n - tmp);
        }
        cout << (ll)n*(n - 1)*(n - 2) / 6 - ans / 2 << endl;
    }
}

然后是不怎么容易理解的莫比乌斯函数——

等会再写解析QAQ

#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<cctype>

#include<iostream>
#include<vector>
#include<map>
#include<queue>
#include<string>
#include<set>
#include<algorithm>
#include<stack>
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define INF 0x7fffffff
#define ll long long
#define eps 1e-5
#define pii pair<int,int>
#define pll pair<ll,ll>
#define m_p make_pair
#define PI 2.0*acos(0.0)
#define MOD 1000000007

#define N 100005

int n, a[N], u[N];
int pri[N], pz;
bool ispri[N];
ll ans, c[N];

void Pre() {
	u[1] = 1;
	for (int i = 2; i < N;i++) {
		if (!ispri[i]) {
			pri[++pz] = i;
			u[i] = -1;
		}
		for (int j = 1; j <= pz;j++) {
			int k = i * pri[j];
			if (k > N) break;
			ispri[k] = 1;
			u[k] = -u[i];
			if (i % pri[j] == 0) { 
				u[k] = 0; 
				break;
			}
		}
	}
}

int main()
{
	Pre();
	int T;
	cin >> T;
	while (T--)
	{
		scanf("%d", &n);
		mem(a, 0);
		mem(c, 0);
		ans = 0;
		int x;
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &x);
			a[x]++;
		}
		for (int i = 2; i < N; i++)
		{
			int num = 0;
			for (int j = i; j < N; j += i)
			{
				num += a[j];
			}
			for (int j = i; j < N; j += i)
			{
				c[j] -= u[i] * num;
			}
		}
		for (int i = 2; i < N; i++)
			c[i] -= a[i];
		for (int i = 1; i < N; i++)
		{
			x = n - a[i] - c[i];
			ans += ((c[i] * a[i]) + (a[i] * (a[i] - 1))) * x;
		}
		cout << (ll)(n)* (n - 1) * (n - 2) / 6 - ans / 2 << endl;
	}

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值