题意:
给出a序列和b序列,定义矩阵M,M(i,j)是a[i]和b[j]的gcd,有q次询问,每次询问(r1, c1)到(r2, c2)这个小矩阵里有多少个不同的gcd。
解题思路:
a序列和b序列的长度都是1e5,所以暴力求解gcd肯定会超时。既然我们没有办法去求出gcd,那么我们可以反过来去验证1e5之内的数是否是这个矩阵里的一个gcd。
具体的做法是,先求出a序列中包含i这个因子的数的个数ma[i],以及b序列中有i这个因子的数的个数mb[i],那么ans[i]=ma[i]×mb[i]就是两个序列中的数的排列组合,这些组合里的数的gcd一定是i的倍数,我们运用下容斥定理把是i的倍数的那些数减去,那么剩下的数就是以i为gcd的数的对数了,公式是 GCDi=Ma[i]∗Mb[i]−∑i|j(ans[j]) ,
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+4;
int a[maxn];
int b[maxn];
long long ma[maxn];
long long mb[maxn];
long long ans[maxn];
int main()
{
int n, m, q;
cin>>n>>m>>q;
int i;
for(i=0; i<n; i++)scanf("%d", &a[i]);
for(i=0; i<m; i++)scanf("%d", &b[i]);
while(q--)
{
memset(ma, 0, sizeof(ma));
memset(mb, 0, sizeof(mb));
memset(ans, 0, sizeof(ans));
int r1, c1, r2, c2, j;
scanf("%d%d%d%d", &r1, &c1, &r2, &c2);
for(i=r1; i<=r2; i++)ma[a[i]]++;
for(i=c1; i<=c2; i++)mb[b[i]]++;
for(i=1; i<=100000; i++)
{
j=i+i;
while(j<=100000)
{
ma[i]+=ma[j];
mb[i]+=mb[j];
j+=i;
}
}
int sum=0;
int num=0;
for(i=100000; i>=1; i--)
{
ans[i]=ma[i]*mb[i];
j=i+i;
while(j<=100000)
{
ans[i]-=ans[j];
j+=i;
}
if(ans[i])sum++;
}
printf("%d\n", sum);
}
}