牛客练习赛44_C:小y的质数 ( 容斥原理 )

题目大意:
由于这是一个区间筛质数的模板题。所以小k不屑于去写。
所以出题人只好yy了另一道题。
定义k生互质数为满足y + k与y - k互质的数。
现在给出区间[L,R],你需要输出区间内k生互质数有多少对 我们说一对k生互质数在区间[L,R]内,当且仅当
y+k ∈ [L,R] 且 y−k ∈ [L,R]。

题目要求求 [l,r] 内 gcd(y,y + 2 * k) = 1 的所有y,因为gcd(y, y + 2 * k) = gcd(y, 2 * k),只需要在[l,r - 2 * k]范围内找满足要求的数就行了。
但是依然很大,暴力是必然会T的,可以用容斥原理,令 r = r - 2 * k(方便表示)。区间[l,r] 的数字总共有 r - l + 1个,要扣掉gcd > 1的情况。
可以分解gcd > 1的情况:对 2 * k 进行素数分解,[l,r] 所有gcd > 1的数字集合中 可能包括 i 种和 2 * k 相同的素因子,枚举一下用容斥原理扣掉,先扣掉包括一种 相同素因子的数的个数, 然后加上 包括 两种相同素因子的数的个数。。。。一路搞到包括所有素因子(容斥原理)。至于有多少个数包含这些数因子,除一下就知道了。

枚举包括几种素因子,可以用枚举子集的方法,先求出 2 * k 所有不同的素数因子集合(不会超过15种),然后可以用二进制枚举子集,或用DFS搜索,复杂度最大 O(15 * 2 ^ 15);
这里的写法是一路从包含0种枚举过去。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll l,r,k;
int cnt = 0;
long long p[30];
int main(){
 	scanf("%lld%lld%lld",&l,&r,&k);
 	k *= 2;
 	r -= k;
 	l -= 1;
 	ll ans = 0;
 	if(l > r){
  		printf("0\n");
  		return 0;
 	}
 	for(ll i = 2; i * i <= k; i++){
  		if(k % i == 0){
   			p[++cnt] = i;
   			while(k % i == 0)
   	 			k /= i;
  		}
 	}	 
 	if(k > 1) p[++cnt] = k;
 	for(int i = 0; i < (1 << cnt); i++){
  		long long an = 1;
  		for(int j = 0; j < cnt; j++){
   			if((1 << j) & i){
    				an *= p[j + 1];
    				an *= -1;
   			}
  		}
  		if(l >= 0)
   			ans += r/an - l/an;
  		else ans += r/an;
 	}
 	printf("%lld\n",ans);
 	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/89421627