首页 > 代码库 > hdu 4135 Co-prime(容斥原理)

hdu 4135 Co-prime(容斥原理)

http://acm.hdu.edu.cn/showproblem.php?pid=4135


求连续区间[a,b]内与n互质的数的个数。

因为a,b相当大,考虑用容斥原理。只需先求出[a,b]内与n不互质的数的个数,等于[1,b]内与n不互质的个数 - [1,a-1]内与n不互质的个数。问题转化为求【1,m】内与n不互质的数的个数。

先对n分解质因子,[1,m]内是n的质因子的倍数的那些数肯定与n不互质,但是有许多重复的,需要减去。质因子解法有多种,队列数组或状态压缩。

例如30的质因子是2,3,5,[1,m]内与30互质的数的个数表示为 n/2 + n/3 + n/5 - n/(2*3) - n/(2*5) - n/(3*5) + n/(2*3*5)。发现质因子个数是奇数的做加法,是偶数的做减法。队列数组解法为模拟一个队列,初始将1加入队列,之后每次取出n的一个质因子依次与队列中的数相乘后置于队尾,每次乘-1决定其前面的正负号。最后队列里的就是上式所有的分子,然后解之。 状态压缩便是将取出的质因子置为1没取出的置为0,得到一个数res,若res的质因子个数是奇数就加上,是偶数就减,求和就是与m不互素的数的个数,解之。


#include <stdio.h>
#include <iostream>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <stack>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#include <stdlib.h>
#include <algorithm>
#define LL __int64
//#define LL long long
#define eps 1e-9
#define PI acos(-1.0)
using namespace std;
const LL INF = 1<<30;
const int maxn = 100010;

LL a,b,n;
LL ans;
int fac[maxn];
int prime[maxn];
int facCnt;

void getPrime()
{
	bool flag[maxn];
	memset(flag,false,sizeof(flag));
	prime[0] = 0;

	for(int i = 2; i < maxn; i++)
	{
		if(flag[i] == false)
			prime[++prime[0]] = i;
		for(int j = 1; j <= prime[0]&&i*prime[j]<maxn; j++)
		{
			flag[i*prime[j]] = true;
			if(i % prime[j] == 0)
				break;
		}
	}
}

void getFac()
{
	LL tmp = n;
	facCnt = 0;
	for(int i = 1; i <= prime[0] && prime[i]*prime[i] <= tmp; i++)
	{
		if(tmp % prime[i] == 0)
		{
			fac[facCnt++] = prime[i];
			while(tmp%prime[i] == 0)
				tmp /= prime[i];
		}
		if(tmp == 1)
			break;
	}
	if(tmp > 1)
		fac[facCnt++] = tmp;
}

LL solve(LL m)
{
	//位运算
	LL anw = 0;
	for(int i = 1; i < (1<<facCnt); i++)
	{
		LL res = 1;
		int cnt = 0;
		for(int j = 0; j < facCnt; j++)
		{
			if(i & (1<<j))
			{
				res *= fac[j];
				cnt++;
			}
		}
		if(cnt & 1)
			anw += m/res;
		else
			anw -= m/res;
	}
	return anw;
}

int main()
{
	int test;
	scanf("%d",&test);
	getPrime();
	for(int item = 1; item <= test; item++)
	{
		scanf("%I64d %I64d %I64d",&a,&b,&n);
		getFac();
		ans = (b-solve(b)) - (a-1-solve(a-1));
		printf("Case #%d: %I64d\n",item,ans);
	}
	return 0;
}




#include <stdio.h>
#include <iostream>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <stack>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#include <stdlib.h>
#include <algorithm>
#define LL __int64
//#define LL long long
#define eps 1e-9
#define PI acos(-1.0)
using namespace std;
const LL INF = 1<<30;
const int maxn = 100010;

LL a,b,n;
LL ans;
int fac[maxn];
int prime[maxn];
int facCnt;

void getPrime()
{
	bool flag[maxn];
	memset(flag,false,sizeof(flag));
	prime[0] = 0;

	for(int i = 2; i < maxn; i++)
	{
		if(flag[i] == false)
			prime[++prime[0]] = i;
		for(int j = 1; j <= prime[0]&&i*prime[j]<maxn; j++)
		{
			flag[i*prime[j]] = true;
			if(i % prime[j] == 0)
				break;
		}
	}
}

void getFac()
{
	LL tmp = n;
	facCnt = 0;
	for(int i = 1; i <= prime[0] && prime[i]*prime[i] <= tmp; i++)
	{
		if(tmp % prime[i] == 0)
		{
			fac[facCnt++] = prime[i];
			while(tmp%prime[i] == 0)
				tmp /= prime[i];
		}
		if(tmp == 1)
			break;
	}
	if(tmp > 1)
		fac[facCnt++] = tmp;
}

LL solve(LL m)
{
	//队列数组
	int que[110];
	int l = 0;
	que[l++] = 1;

	for(int i = 0; i < facCnt; i++)
	{
		int k = l;
		for(int j = 0; j < k; j++)
			que[l++] = fac[i]*que[j]*(-1);
	}

	LL anw = 0;
	for(int i = 0; i < l; i++)
		anw += m/que[i];
	return anw;
}

int main()
{
	int test;
	scanf("%d",&test);
	getPrime();
	for(int item = 1; item <= test; item++)
	{
		scanf("%I64d %I64d %I64d",&a,&b,&n);
		getFac();
		ans = solve(b) - solve(a-1);
		printf("Case #%d: %I64d\n",item,ans);
	}
	return 0;
}





hdu 4135 Co-prime(容斥原理)