HDU1695 欧拉函数+容斥原理+筛质因子

参考:http://hi.baidu.com/myzone2009/blog/item/9a3e7e1895046574dab4bdff.html

题目大意:

给你a, b, c, d, k; 找出这样的一队 x, y, 使得 gcd(x , y) = k, 并且x ∈[1, b], y ∈ [1, d], 问有多少对符合要求的(x, y)。

------------------------------------------------------------------------------

解题思路:

容斥原理 + 筛法 + 欧拉函数。

<容斥原理>

例如,用a, b, c, d, e 五个字母组成长度为5的单词, 要求这写单词不能含有a、 b、 c,问符合要求的单词有多少个。

用容斥原理解这个题。这五个字母所有的排列组合有U = 5^5种。然后,设A[x]表示不含有字母x的单词的排列组合数,其中x ={a, b, c}。设A[x, y]表示不含有字母x和y的单词的排列组合数,其中x,y ={a, b, c}……

那么,符合要求的单词个数为ANS = U - A[a] - A[b] -A[c] + A[a, b] + A[a, c] + A[b, c] - A[a, b, c].

(1)题目要求gcd(x, y) = k , 可以转换为gcd(x / k , y / k) = gcd(n, m) = 1。相应地,题目中,x 和y的范围也可以相应地转换为[1, b / k] (以下以[1,bb]表示)和 [1, d / k](以下以[1,dd]表示)。

(2)题目中,x, y是无序的, 所以,我们可以假设b <= d。

(3)在[1, dd] 中枚举每一个 m ,在[1, min(dd, m - 1)]中找出和 m 互质的数的个数。

①当 m <= bb的时候,可以直接根据欧拉函数求出小于等于m且与m互质的数的个数。

②当 bb <= m <= dd, 要用容斥原理来求。。。  对于m,在[1,bb]总互质的数的个数可以这么求   ans=bb- 求出与其不互质的个数

与其不互质的个数=∑b/(m的一个素因子  )-∑b/(m的两个素因子之积)+∑(b/m的三个素因子之积)······

#include <iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<map>
#include<vector>
using namespace std;
const int N=100009;
typedef __int64 i64;
i64 euler[N+5];//euler[i]保存i的欧拉函数值
vector<int> prime[N+5];//prime[i]保存i的素因子
void EulerFunc()//求出1到N的欧拉函数值并筛出素因子
{
	euler[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(euler[i]==0)
		{
			for(int j=i;j<=N;j+=i)
			{
				if(euler[j]==0)
				euler[j]=j;
				euler[j]=euler[j]*(i-1)/i;
				prime[j].push_back(i);
			}
		}
	}
}
i64 dfs(int st,int b,int now)//容斥原理求出1到b中与now不互质的数目
{
	i64 res=0;
	for(int i=st;i<prime[now].size();i++)
	{
		res+=b/prime[now][i]-dfs(i+1,b/prime[now][i],now);
	}
	return res;
}
int main()
{
	int Case,a,b,c,d,k;
	scanf("%d",&Case);
	EulerFunc();
	for(int ii=1;ii<=Case;ii++)
	{
		scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
		if(k==0)
		{
			printf("Case %d: 0\n",ii);
			continue;
		}
		b/=k;
		d/=k;
		if(b>d)swap(b,d);
		i64 ans=0;
		for(int i=1;i<=b;i++)
		ans+=euler[i];
		for(int i=b+1;i<=d;i++)
		{
			ans+=b-dfs(0,b,i);
		}
		printf("Case %d: %I64d\n",ii,ans);
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值