1. 程式人生 > >hdu 4630 No Pain No Game

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