您的位置:首页 > 其它

BZOJ 2301: [HAOI2011]Problem b(莫比乌斯反演)

2017-07-28 15:02 489 查看
http://www.lydsy.com/JudgeOnline/problem.php?id=2301

题意:
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

思路:

先简单介绍一下莫比乌斯反演在数论中的作用:



那么怎么做这道题呢?



接下来我们只需要枚举d就可以了,但是这里还有一个可以优化的地方,我们依次+1枚举d的时候,有时候n/d和m/d是不会改变的,比如说现在n=m=,那么d=3,4,5时n/d和m/d都是不变的,这样一来的话我们可以分块处理,需要计算一下莫比乌斯的前缀和,就可以将3,4,5的值一起计算了,这样一来,枚举的数量将大大减小。具体看代码。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<sstream>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 5;

int a, b, c, d, k;

bool check[maxn];
int prime[maxn];
int mu[maxn];
int sum[maxn];

void Moblus()
{
memset(check, false, sizeof(check));
mu[1] = 1;
int tot = 0;
for (int i = 2; i <= maxn; i++)
{
if (!check[i])
{
prime[tot++] = i;
mu[i] = -1;
}
for (int j = 0; j < tot; j++)
{
if (i * prime[j] > maxn)
{
break;
}
check[i * prime[j]] = true;
if (i % prime[j] == 0)
{
mu[i * prime[j]] = 0;
break;
}
else
{
mu[i * prime[j]] = -mu[i];
}
}
}
return ;
}

int solve(int n, int m)
{
if(n>m)  swap(n,m);
int ans=0;

for(int i=1,last=0;i<=n;i=last+1)
{
last=min(n/(n/i),m/(m/i));  //分块处理
ans+=(sum[last]-sum[i-1])*(n/i)*(m/i);
}
return ans;
}

int main()
{
//freopen("in.txt","r",stdin);
int T;
Moblus();
sum[0]=0;
for(int i=1;i<=maxn;i++)  sum[i]=sum[i-1]+mu[i];

scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
printf("%d\n",solve(b/k,d/k)-solve(b/k,(c-1)/k)-solve((a-1)/k,d/k)+solve((a-1)/k,(c-1)/k));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: