CX老溼經常被人黑,被黑得多了,自己也就麻木了。於是經常聽到有人黑他,他都會深情地說一句:禽獸啊!
一天CX老溼突發奇想,給大家出了一個難題,並且聲稱誰能夠準確地回答出問題才能繼續黑他,否則他就要反擊了。
這個難題就是:
給出兩個數p和q,接下來q個詢問,每個詢問給出兩個數A和B,請分別求出:
一、有多少個有序數對(x,y)滿足1<=x<=A,1<=y<=B,並且gcd(x,y)為p的一個約數;
二、有多少個有序數對(x,y)滿足1<=x<=A,1<=y<=B,並且gcd(x,y)為p的一個倍數。
只有一組測試資料。
第一行兩個數:p和q。(1<p<10^7 ,1<q<1000。)
接下來有q行,每行兩個數A和B。(1<A,B<10^7)
輸出共q行。每行兩個數。用空格隔開。
分別表示題目描述中的兩個對應的答案。
(x,y)=(2,3)和(x,y)=(3,2)被視為兩個不同有序數對哦!
對於64位整型請用lld,或者cin,cout。T_T
CSU_LQ
CSU Monthly 2013 Oct.
題目連結:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1325
題目分析:倍數直接(a / p) * (b / p)即可,不解釋,主要是約數,開始列舉因子做,各種超時,這題有點類似HDU 4746,考慮預處理出每個因子對答案的貢獻,這裡貢獻指的是它的莫比烏斯函式的係數和,舉個的例子如果p=6,它的因子有1,2,3,6,設F(x)
= (a / x) * (b / x),x為最大公約數,f(x)為x為最大公約數時的組數
f(1) = u(1)F(1 * 1) + u(2)F(1 * 2) + u(3)F(1 * 3) + u(4)F(1 * 4) + u(5)F(1 * 5) + u(6)F(1 * 6) + ...
f(2) = u(1)F(2 * 1) + u(2)F(2 * 2) + u(3)F(2 * 3) + ...
f(3) = u(1)F(3 * 1) + u(2)F(3 * 2) + ...
f(6) = u(1)F(6 * 1) + ...
ans = f(1) + f(2) + f(3) + f(6),我們需要預處理出F函式前面的係數,顯然F(6)的係數為u(6) + u(3) + u(2) + u(1),這些都是它的約數,因此預處理時通過類似篩法的nlogn複雜度即可完成對fac_msum陣列的預處理,fac_msum[i]表示,最終答案裡F(i)前面的係數
光這樣還是不夠的,依然超時,必須再加上分塊求和優化才可通過本題,總複雜度約為p + plog(p) + q*sqrt(p),吐槽一句,單組樣例,1e7的資料,能不memset儘量不memset。。。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define ll long long
using namespace std;
int const MAX = 1e7 + 5;
int p, q, pnum;
short mob[MAX];
bool noprime[MAX];
int pr[MAX], sum[MAX], fac_msum[MAX];
int fac[1000], facnum;
void Mobius()
{
pnum = 0;
mob[1] = 1;
for(int i = 2; i < MAX; i++)
{
if(!noprime[i])
{
pr[pnum ++] = i;
mob[i] = -1;
}
for(int j = 0; j < pnum && i * pr[j] < MAX; j++)
{
noprime[i * pr[j]] = true;
if(i % pr[j] == 0)
{
mob[i * pr[j]] = 0;
break;
}
mob[i * pr[j]] = -mob[i];
}
}
}
void pre()
{
for(int i = 1; i * i <= p; i++)
{
if(p % i == 0)
{
fac[facnum ++] = i;
if(i * i != p)
fac[facnum ++] = p / i;
}
}
for(int i = 0; i < facnum; i++)
for(int j = 1; j * fac[i] < MAX; j++)
fac_msum[j * fac[i]] += mob[j];
for(int i = 1; i < MAX; i++)
sum[i] = sum[i - 1] + fac_msum[i];
}
ll cal(int l, int r)
{
ll ans = 0;
if(l > r)
swap(l, r);
for(int i = 1, last = 0; i <= l; i = last + 1)
{
last = min(l / (l / i), r / (r / i));
ans += (ll) (l / i) * (r / i) * (sum[last] - sum[i - 1]);
}
return ans;
}
int main()
{
Mobius();
scanf("%d %d", &p, &q);
pre();
while(q --)
{
int a, b;
facnum = 0;
ll ans = 0;
scanf("%d %d", &a, &b);
printf("%lld %lld\n", cal(a, b), (ll) (a / p) * (b / p));
}
}