題目大意:
由于這是一個區間篩質數的模闆題。是以小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;
}