1. 程式人生 > >bzoj2694 Lcm(反演)

bzoj2694 Lcm(反演)

Description

這裡寫圖片描述
對於任意的>1的n gcd(a, b)不是n^2的倍數
也就是說gcd(a, b)沒有一個因子的次數>=2

Input

一個正整數T表示資料組數
接下來T行 每行兩個正整數 表示N、M

Output

T行 每行一個整數 表示第i組資料的結果

Sample Input

4
2 4
3 3
6 5
8 3

Sample Output

24
28
233
178

HINT

T <= 10000
N, M<=4000000


分析:
要求gcd(a,b)不能含平方因子,所以gcd(a,b)一定是mu不等於0的數


那麼我們設所有滿足條件的數為p
如果我們列舉這個p,則可以得到式子:
這裡寫圖片描述

實際上這個式子和LCM之和的化簡方式大同小異(只是最開始的列舉元素不同而已)

這裡寫圖片描述

式子中有一個列舉p的愚蠢操作
我們肯定不能無腦列舉啊,所以考慮進一步優化:
這裡寫圖片描述

所以我們只要預處理出:這裡寫圖片描述
就可以解決該問題了

一開始我在想能不能線性篩,
但是不要忘了最開始我們的假設:

p是mu值不等於0的數

所以我們直接列舉mu不等於0的數,用它更新它的倍數,這樣的時間複雜度均攤應該是不超過O(nlogn)

tip

這道題的模數非常特殊,是2的整數次冪,
所以我們直接用int自然溢位,最後取模即可
最後的取%:

ans=(ans%p+p)%p;

(一開始少些了一個+p,就WA掉了,感覺很鬼)

//這裡寫程式碼片
#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

const int N=4000005;
const int p=1<<30;
int n,m,sshu[N],tot=0,mu[N],d[N];
bool no[N];

void make()
{
    mu[1]=1;
    for (int i=2;i<N;i++)
    {
        if
(!no[i]) { sshu[++tot]=i; mu[i]=-1; } for (int j=1;j<=tot&&sshu[j]*i<N;j++) { no[sshu[j]*i]=1; if (i%sshu[j]==0) { mu[i*sshu[j]]=0; break; } mu[i*sshu[j]]=-mu[i]; } } for (int i=1;i<N;i++) if (mu[i]!=0) for (int j=i;j<N;j+=i) d[j]=d[j]+i*(j/i)*(j/i)*mu[j/i]; for (int i=2;i<N;i++) d[i]+=d[i-1]; //字首和 } int sum(int n) { return n*(n+1)/2; } int main() { make(); int T; scanf("%d",&T); while (T--) { scanf("%d%d",&n,&m); int last; int ans=0; for (int i=1;i<=min(n,m);i=last+1) { last=min(n/(n/i),m/(m/i)); ans=ans+sum(n/i)*sum(m/i)*(d[last]-d[i-1]); } ans=(ans%p+p)%p; printf("%d\n",ans); } return 0; }