题意:给你n个数,和m个询问,每个询问有一个l和r和d,现在问你,从【l,r】区间的乘积是不是d的倍数。
思路:对于一个数(k)是不是d的倍数这类问题,我们可以对这两个数分解质因数,之后看看k的质因数和d的质因数之间的关系,如果满足对于d的每一个质因数个数,在k中都有出现过,且k的出现次数要大于等于d的出现次数,这个就是满足的,举个例子,36 和 6 ,36的质因数有 2,2,3,3,6的质因数有 2,3,其中36中2出现的次数大于了6中2出现的次数,36中3出现的次数大于了6中3出现的次数,故36是6的倍数,那么对于这道题,我们需要做的就是先将这n个数分解质因数,对于区间问题我们要怎么解决呢?我们把质因数出现的位置给存下来,比如说我们现在在第3个数,a[3] = 8 ,那么我们开一个vector G[2]里加入 3,3,3,3,之后,之后比如说我们现在要查询l,r的区间是不是d的倍数,我们将d分解质因数之后用二分去查询我们当前的区间里有没有这个值就好了,上代码把,代码比较清晰
代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <math.h>
#include <algorithm>
using namespace std;
const int maxn = 100000+10;
int cnt ;
vector<int>G[maxn];
void p(int id,int x)
{
for(int i = 2 ; i * i <= x ; i++) // 这里的i最大是313
{
while(x % i == 0)
{
G[i].push_back(id);
x = x/i;
}
}
if(x>1) // 这里有一个问题就是 我sqrt(maxn)里的最大的质数其实是313,那么我来了一个2 * 313的时候,我们找到的质因数只有2 ,所以当他没有到1的时候说明我们没有完
{
G[x].push_back(id); // 需要在搞一下
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
int t;
for(int i = 0 ; i < maxn ; i++) G[i].clear();
for(int i = 0 ; i < n ; i++)
{
scanf("%d",&t);
p(i,t); // 分解质因数
}
while(m--)
{
int l,r,p;
scanf("%d%d%d",&l,&r,&p);
l--,r--;
int flag = 0;
cnt = 0;
for(int i = 2 ; i*i<= p ; i ++)
{
cnt = 0;
while(p % i == 0) // 我当前在分解 i这个质因数的
{
cnt++; // 看看我需要多少个i
p = p/i;
}
if(cnt)
{
int pos = upper_bound(G[i].begin() , G[i].end(),r) - // 我在i这个质因数中查看我的 l到r区间有多少个
lower_bound(G[i].begin() , G[i].end(),l);
if(pos < cnt) //如果小于我们现在需要的,就说明不行。
{
flag = 1;
break;
}
}
}
if(p>1) //同理
{
int pos = upper_bound(G[p].begin() , G[p].end(),r) -
lower_bound(G[p].begin() , G[p].end(),l);
if(pos == 0) flag = 1;
}
if(flag) puts("No");
else puts("Yes");
}
}
}
/*
5
3 3
634 319
3
1 1 317
1 2 3
1 3 3
*/