题目
给你n个数,a[],
m个询问,每次询问l,r,d
问[l,r]的乘积,是不是d的倍数
思路来源
钱神
题解
和之前做的一个筛的题很像,
就再总结一下吧
先都分解质因数,
然后对于数ai,ans[ai的质因数q]内放入位置i
注意相同的质因数得重复放入
然后对于d的每个质因数p,
二分查询ans[p]内[l,r]的数有多少,
如果大于等于d的质因数p的数量就可行,
否则不可行
心得
其实感觉题不算难
但喜欢这种思维题的感觉
就归结到思维题里吧
而且觉得自己这次代码写的还不错
算是把线性筛稍微用熟练一点了吧
ans里计位置,res里计多次重复的素因子,
len用于记忆化搜索
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
int T;
int n,m,a[maxn];
int prime[maxn],cnt,len[maxn];
bool ok[maxn];
vector<ll>ans[maxn],res[maxn];
void init()
{
memset(len,-1,sizeof(len));
for(ll i=2;i<maxn;++i)
{
if(!ok[maxn])prime[cnt++]=i;
for(int j=0;i*prime[j]<maxn;++j)
{
ll k=i*prime[j];
ok[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
for(int i=2;i<maxn;++i)
{
int tmp=i,tmp2=i;
for(int j=0;j<cnt;++j)
{
if(prime[j]*prime[j]>tmp)break;
while(tmp%prime[j]==0)
{
res[tmp2].push_back(prime[j]);
tmp/=prime[j];
}
}
if(tmp>1)res[tmp2].push_back(tmp);
}
}
void init2()
{
for(int i=0;i<cnt;++i)
{
ans[prime[i]].clear();
}
}
int main()
{
init();
scanf("%d",&T);
while(T--)
{
init2();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
int tmp=(len[a[i]]==-1?len[a[i]]=res[a[i]].size():len[a[i]]);
for(int j=0;j<tmp;++j)ans[res[a[i]][j]].push_back(i);
}
while(m--)
{
int l,r,d;
scanf("%d%d%d",&l,&r,&d);
int sz=(len[d]==-1?len[d]=res[d].size():len[d]);
//for(int j=0;j<sz;++j)
//printf("%d: ",res[d][j]);
int now=0;bool flag=1;
for(int j=0;j<sz;++j)
{
now++;//一定要加
if(j==sz-1||res[d][j]!=res[d][j+1])//和下一个不等 要清零
{
int num=upper_bound(ans[res[d][j]].begin(),ans[res[d][j]].end(),r)-lower_bound(ans[res[d][j]].begin(),ans[res[d][j]].end(),l);
//printf("%d:%d %d\n",res[d][j],num,now);
if(num<now)
{
flag=0;
break;
}
now=0;
}
}
if(!flag)puts("No");
else puts("Yes");
}
}
return 0;
}