BZOJ 2005: [Noi2010]能量采集(莫比乌斯反演)
http://www.lydsy.com/JudgeOnline/problem.php?id=2005
题意:
思路:
首先要知道一点是,某个坐标(x,y)与(0,0)之间的整数点的个数为gcd(x,y),这样一来每个坐标损失的能量为2*gcd(x,y)-1。
所以在这道题目中要计算的就是
f(d)表示gcd(x,y)=d的对数,那么F(d)表示d|gcd(x,y)的对数。
根据反演可以得到,
那么这道题的答案就是,
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<sstream> 6 #include<vector> 7 #include<stack> 8 #include<queue> 9 #include<cmath> 10 #include<map> 11 #include<set> 12 using namespace std; 13 typedef long long ll; 14 typedef pair<int,int> pll; 15 const int INF = 0x3f3f3f3f; 16 const int maxn = 100000 + 5; 17 18 bool check[maxn]; 19 int prime[maxn]; 20 int mu[maxn]; 21 ll sum[maxn]; 22 23 void Mobius() 24 { 25 memset(check, false, sizeof(check)); 26 mu[1] = 1; 27 int tot = 0; 28 for (int i = 2; i <= maxn; i++) 29 { 30 if (!check[i]) 31 { 32 prime[tot++] = i; 33 mu[i] = -1; 34 } 35 for (int j = 0; j < tot; j++) 36 { 37 if (i * prime[j] > maxn) 38 { 39 break; 40 } 41 check[i * prime[j]] = true; 42 if (i % prime[j] == 0) 43 { 44 mu[i * prime[j]] = 0; 45 break; 46 } 47 else 48 { 49 mu[i * prime[j]] = -mu[i]; 50 } 51 } 52 } 53 sum[0]=0; 54 for(int i=1;i<maxn;i++) 55 sum[i]=sum[i-1]+mu[i]; 56 return ; 57 } 58 59 ll solve(int n, int m) 60 { 61 if(n>m) swap(n,m); 62 ll tmp=0; 63 for(int i=1,last=0;i<=n;i=last+1) 64 { 65 last=min(n/(n/i),m/(m/i)); 66 tmp+=(sum[last]-sum[i-1])*(n/i)*(m/i); 67 } 68 return tmp; 69 } 70 71 int n, m; 72 73 int main() 74 { 75 //freopen("in.txt","r",stdin); 76 Mobius(); 77 while(~scanf("%d%d",&m,&n)) 78 { 79 ll ans=0; 80 for(int i=1;i<=min(n,m);i++) //枚举d 81 ans+=solve(n/i,m/i)*i; //这儿求gcd(x,y)=d的对数,但是如果/i的话就相当于计算gcd(x,y)=1的对数 82 //简化了计算 83 printf("%lld\n",2*ans-(ll)n*m); 84 } 85 return 0; 86 }