http://www.spoj.com/problems/PGCD/
题意: 给出 a ,b (1<=a,b <=10^7) ,求 gcd(x,y) =prime 的 方案数, 其中 1=<x<=a , 1<=x<=b 。
如果gcd(x ,y) = 1; 这个问题比较好求。
g(n) 为 gcd(x ,y) = n 的方案数。
f( n ) 为 gcd(x ,y) = b ,其中 n|b 的所有方案数
f( a ) = sigma( g( b )) 条件 a|b;
g( n ) = sigma( u(b)* f(b) ) n|b
u(b) =1 , b 是 Square-free Number 且 b是偶数个素因子的积
u(b) = 0 b 是 Square-free Number 且 b是寄数个素因子的积
u(b) = -1 b 是 不是 Square-free Number
f( n ) = a/n * b/n
g( n ) = sigma( u(t/n )*a/t* b/t);
ans = sigma( g( d )) ,d 是素数
ans = sigma{ u[ j ]* a/( d* j)* b/( d *j ) } d 是素数。
对于 x=p1^k1 * p2^k2 * p3^k3 ... 其中pi为素数。
当所有 ki全为1。那么从里面拿出一个素数d,会剩下一个Square-Free-Number。
1设x有t个素因子。取出一个后(其实就是 对于(a,b) 中, gcd(n)= 取值范围(a / n, b/ n) 中的gcd( 1) )而取法有t种于是ans+= ( ((t-1)&1)?-1:1 ) * t * (m/x) * (n/x)
2 当ki有一个是2,其它全是1 时, 所取的值只能是 ki等于2 的那个数,其余的都不会构成 square-free Number 。 所以取法仅一种。
那么显然 ans+=( (t&1)?-1:1 )*(m/x)*(n/x)
3 对于有多个2 的 x 不用考虑, 无论取那个都不是 square-free Number。
解法 对 u 进行打表, 求解过程中 分块求求值。
打表过程比较慢 程序 45s 。。 不解,本地跑得挺快的。。
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <cstring>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <assert.h>
#include <queue>
#define REP(i,n) for(int i=0;i<n;i++)
#define TR(i,x) for(typeof(x.begin()) i=x.begin();i!=x.end();i++)
#define ALLL(x) x.begin(),x.end()
#define SORT(x) sort(ALLL(x))
#define CLEAR(x) memset(x,0,sizeof(x))
#define FILLL(x,c) memset(x,c,sizeof(x))
using namespace std;
const double EPS = 1e-8;
#define LL long long
#define pb push_back
const int maxn = 10000001;
int f[maxn];
int sum[maxn];
int two[maxn];
void init(){
for(int i =1 ;i<maxn;i++){
f[i] = 0;
two[i]=0;
}
for(int i=2;i<maxn;i++){
if(!f[i]){
for(int j = i;j<maxn;j+=i){
f[j] = i;
}
}
}
for(int i=2;i<maxn;i++){
if(f[i] ==0){
f[i] =1;
}else{
int j = i/f[i];
if(j%f[i] ==0){
two[i] = two[j]+1;
}else{
two[i] = two[j];
}
f[i] = f[j]+1;
}
}
for(int i=2;i<=maxn;i++){
if(two[i] == 1){
if(f[i]&1){
f[i] =1;
}else{
f[i] = -1;
}
}else if(two[i] == 0){
if((f[i])&1){
}else{
f[i] = -f[i];
}
}else{
f[i] = 0;
}
}
sum[0]= 0;
sum[1] = 0;
for(int i=2;i<maxn;i++){
sum[i] = sum[i-1] + f[i];
}
}
int a,b, c;
LL ans;
void gcd(int a,int b){
int k = min(a,b);
for(int i = k , j ; i > 1 ; i = j ){
LL ta = a/i, tb = b/i;
j =max(a/ (ta+1) , b /(tb+1));
ans += (LL)ta*tb*(sum[i] - sum[j]);
// cout << ta<<" "<<tb<< " "<<sum[i] - sum[j]<<endl;
}
}
int main(){
init();
int t ;
cin>>t;
while(t--){
scanf("%d%d",&a,&b);
ans = 0;
gcd(a,b);
printf("%lld\n",ans);
}
return 0;
}