hdu6287 口算训练(思维+筛+二分)

题目

给你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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值