hdu 4630 No Pain No Game

老师说你们做题太少了,这都是套路题。。考了居然都不会。。。

面壁ing。。。

我回来了。。。

好吧它确实是套路题。。。题目要求给一段1-n的序列。。然后每次询问l-r区间中最大的gcd。。。询问1e5,序列1e5.。

自然需要log级的。。根号也可以。毕竟1e5的根号还可以过的。。

我们发现某些数据结构可以来处理每个数的因数在那些地方出现过。比如树状数组(比如主席树)。

然后就可以在这个数出现的时候把它的约数上次出现的地方的最大值更新一下。也就代表他的贡献从上次出现开始都有。

这样把询问离线处理,排序之后每次到达这个位置如果有询问就可以直接处理

但是如果正向处理的话,就很难求出区间的最值(我懒得敲线段树),我们追求最简单写法。。

所以逆向处理之后倒着插入,这样前缀最大值就是目前最大值。。所以确实很套路,也很简单。。。

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int val[100005];
int n,Q,T;
int c[100005];
int las[100005];
int ans[100005];
struct node
{
	int l1;
	int r1;
	int pos;
}nnd[100005];
int tt=1;
int cmp(node a,node b)
{
	return a.l1>b.l1;
}
int lowbit(int x)
{
	return x&-x;
}
void add(int x,int v)
{
	while(x<=n)
	{
		c[x]=max(c[x],v);
		x+=lowbit(x);
	}
}
int gett(int x)
{
	int s=0;
	while(x)
	{
		s=max(s,c[x]);
		x-=lowbit(x);
	}
	return s;
}
void findf(int x,int po)
{
	for(int i=1;i*i<=x;i++)
	{
		if(x%i==0)
		{
			if(!las[i])
			{
				las[i]=po;
			}else
			{
				add(las[i],i);
				las[i]=po;
			}
			if(i*i==x)continue;
			if(!las[x/i])
			{
				las[x/i]=po;
			}else
			{
				add(las[x/i],x/i);
				las[x/i]=po;
			}
		}
	}
}
void init()
{
	memset(las,0,sizeof(las));
	memset(c,0,sizeof(c));tt=1;
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		init();
		scanf("%d",&n);
		for(int i=1;i<=n;i++)scanf("%d",&val[i]);
		scanf("%d",&Q);
		for(int i=1;i<=Q;i++)
		{
			scanf("%d%d",&nnd[i].l1,&nnd[i].r1);
			nnd[i].pos=i;
		}
		sort(nnd+1,nnd+1+Q,cmp);
		for(int i=n;i>=1;i--)
		{
			findf(val[i],i);
			while(nnd[tt].l1==i)
			{
				ans[nnd[tt].pos]=gett(nnd[tt].r1);
				tt++;
			}
		}
		for(int i=1;i<=Q;i++)
		{
			printf("%d\n",ans[i]);
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值