POJ 2773 Happy 2006 二分 and 容斥原理 or 欧拉函数

POJ 2773

题意 : 求与n互质的第k大的数

容斥原理:
1到m中与n互质的个数为 m - (与n不互质的数的个数)    于是根据容斥原理,首先求出n的所有的素因子,对于1到m,
与n不互质的数为 m - (m以内所有一个素因子构成的因子的个数) + (m以内所有两个素因子构成的因子的个数) - (m以内所有三个素因子构成的因子的个数)。。。
刚开始wrong answer了两次,原来居然把二分的范围限制在了1到n,后来改成10^9就AC了


欧拉函数:

phi(n)表示1到n中与n互质的数的个数 , 区间(1 , n)与 区间(n+1 , 2n)中与n互质的数都是一一对应的,这个很容易得到,有了这个循环节,我们就可以枚举1到n求出第k%phi(n)个互质数就行了


容斥原理

#include <stdio.h>
int f[222];
int change , top , k , d[222] ;
const int maxn = 1000000000;
int gcd(int a,int b)
{
	return b ? gcd(b,a%b) : a;
}
void rongchi(int now,int num,int tot,int x)  // now当前处理到的素因子,num当前有的素因子个数,总的素因子个数
{
	// 以前写搓了,直接就用二进制压缩位来求多简单。。。现在就不改了
	int i;
	if(num==tot)
	{
		int sum = 1;
		for(i = 0;i < num ;i++)
			sum *= d[i];
		change += x/sum;
	}
	else
		for(i = now;i < top; i++)
		{
			d[num] = f[i];
			rongchi(i+1,num+1,tot,x);
		}
}
int bin(int n,int tot,int l,int r)
{
	int i,j;
	while(l<=r)
	{
		int mid = (l+r)>>1;
		for(i = 0;i < tot; i++)
			if(f[i]>mid)
				break;
		int sum = mid;
		top = i;
		for(j = 0;j < i ;j++)   //    求出mid以内与m互质的个数
		{
			change = 0;
			rongchi(0,0,j+1,mid);
			if(j&1)
				sum += change;
			else
				sum -= change;
		}
	//	printf("%d %d %d %d\n",l,mid,r,sum);
		if(sum == k)
		{
			for(i = mid;i >= 1; i--)
				if(gcd(n,i)==1)     // 找到mid以内第k个与m互质的数
					return i;
		}
		if(sum > k)
			r = mid-1;
		else
			l = mid+1;
	}
}
int main()
{
	int n, i;
	while(scanf("%d%d", &n, &k)!=-1)
	{
		int m = n;
		int tot = 0;
		for(i = 2;i*i <= n ;i++)
		{
			if(n%i==0)
			{
				while(n%i==0)
					n /= i;
				f[tot++] = i;   // 预处理出素因子
			}
		}
		if(n!=1)
			f[tot++] = n;
		printf("%d\n",bin(m,tot,1,maxn));
	}
	return 0;
}


欧拉函数

#include <stdio.h>
int f[111];
int gcd(int a,int b){
	return b ? gcd(b , a%b) : a;
}
int main()
{
	int n,k,i;
	while(scanf("%d%d", &n ,&k)!=-1)
	{
		if(n == 1)
		{
			printf("%d\n", k);
			continue;
		}
		int m = n;
		int tot = 0;
		for(i = 2;i*i <= m ;i ++)
			if(m%i == 0)
			{
				while(m%i==0)   // 筛选素因子
					m /= i;
				f[tot++] = i;
			}
		if(m != 1)
			f[tot++] = m;
		int sum = n;
		for(i = 0;i < tot; i++)
			sum = sum - sum/f[i];
		if(k%sum == 0) // 如果k刚好是phi(n)的倍数,就只需要找出第phi(n)个互质数
		{
			for(i = n-1;i >= 1; i--)
				if(gcd(i , n)==1)
					break;
			printf("%d\n", (k/sum-1)*n+i);
		}
		else
		{
			int ans = k/sum*n;
			k = k%sum;
			for(i = 1;i <  n && k>0; i++) // 枚举找到第k%phi(n)个数
				if(gcd(i , n) == 1)
					k--;
			printf("%d\n" , ans + i-1);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值