HDU6287:口算训练( 二分 + 思维 + 分解质因数 )

目录

一、题意

二、思路


Problem Description

小Q非常喜欢数学,但是他的口算能力非常弱。因此他找到了小T,给了小T一个长度为n的正整数序列a1,a2,...,an,要求小T抛出m个问题以训练他的口算能力。
每个问题给出三个正整数l,r,d,小Q需要通过口算快速判断al×al+1×...×ar−1×ar是不是d的倍数。
小Q迅速地回答了出来,但是小T并不知道正确答案是什么,请写一个程序帮助小T计算这些问题的正确答案。

Input

第一行包含一个正整数T(1≤T≤10),表示测试数据的组数。
每组数据第一行包含两个正整数n,m(1≤n,m≤100000),分别表示序列长度以及问题个数。
第二行包含n个正整数a1,a2,...,an(1≤ai≤100000),表示序列中的每个数。
接下来m行,每行三个正整数l,r,d(1≤l≤r≤n,1≤d≤100000),表示每个问题。

Output

对于每个问题输出一行,若是倍数,输出Yes,否则输出No。

Sample Input

1

5 4

6 4 7 2 5

1 2 24

1 3 18

2 5 17

3 5 35

Sample Output

Yes

No

No

Yes

一、题意

        给你n个数和m个询问,每个询问有一个 l,r,d 。提问, [ l,r ] 的区间的乘积是不是d的倍数。

二、思路

        我们可以发现,一个数( a )(比如 30 ),问它是否是 d(比如 6 )的倍数,我们可以发现, 30 = 2 * 3 * 5 ,而 6 = 2 * 3 。我们发现, 6 的质因数 30 都有,或大于,或等于。那么,我们可以把两个数进行质因数分解,然后判断 d 的质因数 a 是否都有,且 a 的每个质因数的数量都要大于等于 k 的。

        那么,我们第一步要做的,就是把 n 个数都分解质因数。

        区间的问题我们怎么解决呢?我们可以把每个质因数( i )的位置( id )都记录下来,是

v [ i ].push_back ( id )

        当我们查找区间 [ l , r ] 中某个质因数时,我们把 d 分解质因数,然后二分查找区间内有没有这个值,有的话我们再判断数量的大小就行了。

        二分的话,我们可以用到 “ lower_bound ” 与 “ upper_bound ” 这两个函数。 lower_bound 是返回第一个大于等于 x 值的位置 , upper_bound 是返回第一个大于val值的位置。我们要查找大于等于 l 到小于等于 r 区间的质因数,我们就可以看看:

         1 …… …… R+1 …… n

        其中,蓝色是 upper_bound返回的位置,黄色是我们要计算的右区间,绿色是黄色与 lower_bound(蓝) 的混合。我们要计算 L 到 R 的区间,一共有 R - L + 1个数,而计算 upper_bound - lower_bound 是 R + 1 - L,一样的。

        代码:

#include<bits/stdc++.h>
using namespace std;
int read()//快读 
{
	char ch=getchar();
	int x=0,f=1;
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
		{
			f=-1;
			ch=getchar();
		}
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
vector<long long> v[100005];
void prime(long long id,long long x)//分解质因数 
{
	for(long long i=2;i*i<=x;i++)
	{
		while(x%i==0)
		{
			v[i].push_back(id);
			x/=i;
		}
	}
	if(x>1)//本身是质因数 
	{
		v[x].push_back(id);
	}
}
int main()
{
	long long t;
	t=read();
	while(t--)
	{
		long long n,m;
		n=read();
		m=read();
		for(long long i=1;i<=100005;i++)
		{
			v[i].clear();
		}
		for(long long i=1;i<=n;i++)
		{
			long long x;
			x=read();
			prime(i,x);
		}
		while(m--)
		{
			long long l,r,d;
			l=read();
			r=read();
			d=read();
			bool flag=true;
			for(long long i=2;i*i<=d;i++)//分解d 
			{
				long long sum=0;//计数 
				while(d%i==0)
				{
					sum++;
					d/=i;
				}
				if(sum)
				{
					if(sum>upper_bound(v[i].begin(),v[i].end(),r)-lower_bound(v[i].begin(),v[i].end(),l))
					{
						flag=false;
						break;
					}
				}
			}
			if(flag==true&&d>1&&upper_bound(v[d].begin(),v[d].end(),r)-lower_bound(v[d].begin(),v[d].end(),l)<=0)
			{
				flag=false;
			}
			if(flag)
			{
				cout<<"Yes"<<endl;
			}
			else
			{
				cout<<"No"<<endl;
			}
		}
	}
	return 0;
}
  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值