最大公约数(gcd)

最大公约数($gcd$)
**【题目描述】** 给出$ n $个正整数$ai$, 标号$ 1,2,…,n$。 有$ m $个询问。 每个询问包含三个参数$ g$,$l$,$r$, 表示询问第$ l $个到第$ r $个数满足与$ g $的最大公约数大于$ 1$的所有数中, 最大的数是多少, 并统计有多少个最大的数。 **【输入格式】** 第一行两个正整数 $n,m$, 表示正整数的数量和询问的数量。 第二行$ n $个正整数, 表示$a_i$。 接下来$ m $行, 每行三个正整数$ g,l,r$。 **【输出格式】** 对于每个询问, 输出一行答案, 包括两个数, 分别是最大的数以及出现次数。 如果不存在, 则输出$-1$ $-1$。 **【输入样例】** $6$ $5$ $1$ $2$ $3$ $4$ $5$ $4$ $2$ $1$ $5$ $121$ $1$ $6$ $3$ $2$ $6$ $5$ $5$ $5$ $24$ $4$ $6$ **【输出样例】** $4$ $1$ $-1$ $-1$ $3$ $1$ $5$ $1$ $4$ $2$ **【 数据范围与约定】** 对于 $100\%$的数据, $1 ≤ n,m,g,ai ≤ 100000,1 ≤ l ≤ r ≤ n$ |数据编号 |$n$,$m$|其它| | ------------- |-------------| -----| | $0$~$1$ | $≤ 1000$ | | $2$~$4$|$ ≤ 10^5 $ |$l=r$ | | $5$~$6$ |$ ≤ 10^5 $ | 所有$a_i$,$g$ 都是偶数 | |7~9|$ ≤ 10^5 $ | **题解:** 对于每一个素数,将所有是这个素数的倍数的数加入线段树中,最多大约$10000$棵线段树,所以要动态开点,求出$max$后再建一棵线段树统计答案出现的次数。 $Code:$
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 100005
using namespace std;
int treemaxnum[N*200],treemaxlson[N*200],treemaxrson[N*200];
int treenumnum[N*20],treenumlson[N*20],treenumrson[N*20];
int prime[N],pri[N],pre[N],pred[N],n,m,a[N],p[N];
int cntmax,cntnum,rootmax[N],rootnum[N];
void buildmax(int &o,int l,int r,int x,int y)
{
	if(!o)o=++cntmax;
	if(l==r)
	{
		treemaxnum[o]=max(treemaxnum[o],y);
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)buildmax(treemaxlson[o],l,mid,x,y);
		else buildmax(treemaxrson[o],mid+1,r,x,y);
	treemaxnum[o]=max(treemaxnum[treemaxlson[o]],treemaxnum[treemaxrson[o]]);
}
int querymax(int o,int l,int r,int x,int y)
{
	if(!o)return -1;
	if(l==x&&r==y)return treemaxnum[o];
	int mid=(l+r)>>1;
	if(y<=mid)return querymax(treemaxlson[o],l,mid,x,y);else
		if(x>mid)return querymax(treemaxrson[o],mid+1,r,x,y);else
			return max(querymax(treemaxlson[o],l,mid,x,mid),querymax(treemaxrson[o],mid+1,r,mid+1,y));
}
void buildnum(int &o,int l,int r,int x)
{
	if(!o)o=++cntnum; 
	if(l==r)
	{
		treenumnum[o]++;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)buildnum(treenumlson[o],l,mid,x);
		else buildnum(treenumrson[o],mid+1,r,x);
	treenumnum[o]=treenumnum[treenumlson[o]]+treenumnum[treenumrson[o]];
}
int querynum(int o,int l,int r,int x,int y)
{
	if(!o)return 0;
	if(l==x&&r==y)return treenumnum[o];
	int mid=(l+r)>>1;
	if(y<=mid)return querynum(treenumlson[o],l,mid,x,y);else
		if(x>mid)return querynum(treenumrson[o],mid+1,r,x,y);else
			return querynum(treenumlson[o],l,mid,x,mid)+querynum(treenumrson[o],mid+1,r,mid+1,y);
}
int main()
{
	freopen("gcd.in","r",stdin);
	freopen("gcd.out","w",stdout);
	memset(prime,true,sizeof(prime));
	prime[1]=false;
	int num=0;
	treemaxnum[0]=-1;
	for(int i=2;i<=N;i++)
	{
		if(prime[i])
		{
			pri[++num]=i;
			pre[i]=1;
			pred[i]=num;
		}
		for(int j=1;j<=num&&i*pri[j]<=N;j++)
		{
			prime[i*pri[j]]=false;
			pre[i*pri[j]]=i;
			if(i%pri[j]==0)break;
		}
	}
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		int k=a[i];
		num=0;
		while(k!=1)
		{
			p[++num]=k/pre[k];
			k=pre[k];
		}
		sort(p+1,p+1+num);
		num=unique(p+1,p+num+1)-p-1;
		for(int j=1;j<=num;j++)
			buildmax(rootmax[pred[p[j]]],1,n,i,a[i]);
	}
	for(int i=1;i<=n;i++)
		buildnum(rootnum[a[i]],1,n,i);
	while(m--)
	{
		int g,l,r;
		scanf("%d%d%d",&g,&l,&r);
		int k=g,mx=-1,sum=0;
		num=0;
		while(k!=1)
		{
			p[++num]=k/pre[k];
			k=pre[k];
		}
		sort(p+1,p+1+num);
		num=unique(p+1,p+num+1)-p-1;
		for(int j=1;j<=num;j++)
			mx=max(mx,querymax(rootmax[pred[p[j]]],1,n,l,r));
		if(mx==-1)puts("-1 -1");else
		{
			sum=querynum(rootnum[mx],1,n,l,r);
			printf("%d %d\n",mx,sum);
		}
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JackflyDC

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

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

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

打赏作者

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

抵扣说明:

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

余额充值