HDU3388 - Coprime(容斥+二分)


Description

Please write a program to calculate the k-th positive integer that is coprime with m and n simultaneously. A is coprime with B when their greatest common divisor is 1.
 

Input

The first line contains one integer T representing the number of test cases.
For each case, there's one line containing three integers m, n and k (0 < m, n, k <= 10^9).
 

Output

For each test case, in one line print the case number and the k-th positive integer that is coprime with m and n.
Please follow the format of the sample output.
 

Sample Input

3 6 9 1 6 9 2 6 9 3
 

Sample Output

Case 1: 1 Case 2: 5 Case 3: 7
 

题意
给两个数a, b。求与它们俩均互质的第n个数是哪个
和其它类似的容斥题很类似,唯一多了个二分求解,总的来说算一个难点的水题吧

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll maxs = 1e10;
const int maxn = 1e5 + 5;
int prime[maxn];
int a1[maxn], a2[maxn], buf[maxn];
bool vis[maxn];
int l1, l2, tl;
int a, b, n;

void setin() { //求素数 
	int cnt = 0;
	memset(vis, false, sizeof(vis));
	for(int i=2;i<maxn;i++) if(!vis[i]) {
		prime[cnt++] = i;
		for(ll j=(ll)i*(ll)i;j<maxn;j+=(ll)i) vis[j] = true;
	}
}

void ext(int x, int* s, int& l) { //提取x的质因子 
	int lim = sqrt(x); l = 0;
	for(int i=0;prime[i]<=lim;i++) {
		if(x%prime[i]==0) s[l++]=prime[i];
		while(x%prime[i]==0) x/=prime[i];
		if(x == 1) break;
	}
	if(x != 1) s[l++] = x;
}

void unit(int *s1, int* s2, int& l) { //归并两数的质因子,应该也可以用set,当时担心超时就自己写了一个 
	int le1=0, le2=0; l = 0;
	sort(s1,s1+l1);
	sort(s2,s2+l2);
	while(le1<l1 || le2<l2) {
		if(le1>=l1 || (le2<l2 && s2[le2]<=s1[le1])) {
			if(le1<l1 && s2[le2]==s1[le1]) le1++;
			buf[l++] = s2[le2++];
		}
		else
			buf[l++] = s1[le1++];
	}
	for(int i=0;i<l;i++) s1[i] = buf[i];
}

bool ok_hack(ll x) { //判断0-x中是否含n个或n个以上符合条件的数,方便二分 
	ll has = 0;
	for(ll s=1;s<(1<<tl);s++) {
		int cnt = 0;
		ll mul = 1;
		for(int i=0;i<tl;i++) if(s & (1<<i)) {
			cnt ++;
			mul *= (ll)a1[i];
		}
		if(cnt&1)
			has += x/mul;
		else
			has -= x/mul;
	}
	has = x - has;
	return has >= (ll)n;
}

int main() {
	setin();
	int T, kase = 1;
	scanf("%d", &T);
	while(T--) {
		scanf("%d%d%d", &a, &b, &n);
		ext(a, a1, l1); 
		ext(b, a2, l2);
		unit(a1, a2, tl);
		ll l = 1, r = maxs;
		while(l < r) {//二分求解
			ll mid = l+(r-l)/2;
			if(ok_hack(mid)) r=mid;
			else l=mid+1;
		}
		printf("Case %d: %lld\n", kase++, l);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值