（赛前练手 #9） BZOJ2005 [Noi2010]能量采集（容斥原理）

2005: [Noi2010]能量采集
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 5041 Solved: 3078
[Submit][Status][Discuss]
Description

Input

Output

Sample Input
【样例输入1】

5 4

【样例输入2】

3 4

Sample Output
【样例输出1】

36

【样例输出2】

20

HINT
Source

①莫比乌斯反演 + 整除分块。做的时候考虑过但还是不会推。。所以请大家还是自己搜一下反演的做法吧
②容斥原理。曾经做过一道n = m的题，于是把那道题的方法套用过来，其实对于gcd(x , y) = k来说(x <= n && y <= m)，一定也有gcd(x / k , y / k) = 1，那么我们就可以先枚举x的因数，也就是k，然后运用质因数分解和容斥原理来筛有多少对数满足它们的gcd为k
AC Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define rg register
#define il inline
#define maxn 200005
#define ll long long
using namespace std;
bool vis[maxn];
int primes[maxn] , f[maxn] , phi[maxn] , qq[maxn] , q[maxn] , cnt , tot , tot2;
ll n , m;
rg ll x = 0 , w = 1 ;
rg char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9'){
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar();
}
return x * w;
}
void prime(int n){
for (rg int i = 2 ; i <= n ; ++i){
if (!vis[i]) primes[++cnt] = i , phi[i] = i - 1;
for (rg int j = 1 ; j <= cnt && i * primes[j] <= n ; ++j){
vis[i * primes[j]] = 1;
if (!(i % primes[j])){
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
}
void fenjie(int x){
tot = 0;
rg int tmp = x , i = 1;
while (vis[tmp]){
if (!(tmp % primes[i])){
q[++tot] = primes[i];
tmp /= primes[i];
}
while (!(tmp % primes[i]))
tmp /= primes[i];
++i;
}
if (tmp != 1){
q[++tot] = tmp;
}
}
void fenjie2(int x){
tot2 = 0;
rg int sqx = sqrt(x);
for (rg int i = 1 ; i <= sqx ; ++i){
if (!(x % i)){
qq[++tot2] = i , qq[++tot2] = x / i;
if (i == x / i) --tot2;
}
}
}
int rongchi(int x){
f[0] = -1;
rg int ans = 0;
cnt = 0;
rg int tmp = 0;
for (rg int i = 1 ; i <= tot ; ++i){
tmp = cnt;
for (rg int j = 0 ; j <= tmp ; ++j){
f[++cnt] = f[j] * q[i] * (-1);
}
}
for (rg int i = 1 ; i <= cnt ; ++i)
ans += x / f[i];
return x - ans;
}
int gcd(int a , int b){
if (!b) return a;
return gcd(b , a % b);
}
int min(int a , int b){
return (a < b) ? a : b;
}
int max(int a , int b){
return (a > b) ? a : b;
}
int main(){
prime(max(n , m));
if (n > m) n ^= m , m ^= n , n ^= m;
ll ans = m , tmp;
for (rg int i = 2 ; i <= n ; ++i){
tmp = 0;
if (!vis[i]) {
ans += m / i * i + m - m / i;
continue;
}
fenjie2(i);
for (rg int j = 1 ; j <= tot2 ; ++j){
fenjie(i / qq[j]);
tmp = rongchi(m / qq[j]);
ans += qq[j] * tmp;
}
}
printf("%lld" , ans * 2 - n * m);
return 0;
}


©️2019 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客