题目大意:给出一个整数x,和n次询问,每次询问给出一个区间[l.r],问这个区间里有多少个数k满足gcd(k*x^x,x)=1
1<=x<=1e6;1<=n<=1e5;1<=l<=r<=1e12
,思路:通过打表可以发现gcd的值是循环的,例如x=3时,所有gcd的值就是以3,1,1,3这四个数为循环节循环,同时我们发现循环节的长度就等于2的log2(向上取整)次方,那么我们事先算出循环节中的每一个数,同时用前缀和统计1的数量,这样要求一个区间内有多少个1,只需用sum[r]-sum[l-1],每次询问时,我们先加上l到它右边的一个循环节的数,然后加上r到他左边的的循环节的数,同时维护l和r到循环节的端点处,然后再加上(r-l+1)/循环节长度*区间内所有数的和
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 5e6 + 5;
ll sum[N];
ll gcd(ll a, ll b)
{
return a == 0 ? b : gcd(b % a, a);
}
int main()
{
ll x, n;
cin >> x >> n;
ll inter = 1;//区间长度
while (inter < x)
inter <<= 1;//2的log2x(向上取整)次方
for (ll i = 1; i <= inter; i++)
{
ll num = gcd((i * x) ^ x, x) == 1;//判断循环节内每一个数是否为1
sum[i] = num + sum[i - 1];//前缀和记录
}
for (ll i = 1; i <= n; i++)
{
ll l, r;
scanf("%lld%lld", &l, &r);
ll ans = 0;
if ((l-1) % inter)
{//l到右边的循环节
ll temp = (l - 1) % inter;//比循环节多出来的长度
ans += sum[inter] - sum[temp];//用前缀和求有多少个1
l += inter-temp;//维护算完的左边界
}
if (r % inter)
{//r到左边的循环节
ll temp = r % inter;
ans += sum[temp] - sum[0];
r -= temp;
}
ans += ((r - l + 1) / inter) * (sum[inter] - sum[0]);//剩余的整循环节
printf("%lld\n", ans);
}
return 0;
}