1. 程式人生 > >noip2018 雅禮模擬賽day1 T1

noip2018 雅禮模擬賽day1 T1

分塊...

首先我們可以看到,對於任意的模數k,所有的可能答案一定會在所有k的整數倍的前驅處取到

證明:假設所有值均小於k,那麼我們顯然答案是這其中的最大值

假設所有值均小於2k,那麼我們將這些值分成兩部分,小於k的部分中的最大值即為取模k的最大值,而在[k,2k]之間的部分取模k即相當於-k,所以與2k最接近的值即為取模k的最大值,兩者取最大即為整體取模k的值

那麼我們對整個序列進行分塊,對每個塊預處理出對每個所有k取模的最大值,這樣在查詢時塊內的部分O(1)即可

而再根據上面的思想,我們可以知道:在處理最大值時,只需找出k的所有整數倍,求出在塊中小於他的最大值,然後對所有這樣的值取模k比較即可

這樣時間複雜度即為調和級數求和,接近klnk

然後查詢即可,將分塊大小設為1000能獲得最佳時間複雜度,但常數巨大,必須開O2

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
const int siz=999;
int a[100005];
int f[205][100005];
int temp[100005];
int n,m;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	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;
}
int main()
{
	freopen("flower.in","r",stdin);
	freopen("flower.out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
	}
	int cnt=(n-1)/siz+1;
	for(int i=1;i<=cnt;i++)
	{
		 memset(temp,0,sizeof(temp));
		 for(int j=siz*(i-1)+1;j<=min(siz*i,n);j++)
		 {
 			temp[a[j]]=a[j];
 		}
 		for(int j=1;j<=100001;j++)
 		{
		 	temp[j]=max(temp[j],temp[j-1]);
		 }
		 for(int j=1;j<=100001;j++)
		 {
			for(int k=0;k<=100001;k+=j)
			{
				f[i][j]=max(f[i][j],temp[min(k+j-1,100001)]-k);
			}
 		}
	}
	for(int i=1;i<=m;i++)
	{
		int l=read(),r=read(),k=read();
		int lq=(l-1)/siz+1;
		int rq=r/siz;
		int ret=0;
		if(rq<=lq+1)
		{
			for(int j=l;j<=r;j++)
			{
				ret=max(ret,a[j]%k);
			}
		}else
		{
			for(int j=l;j<=lq*siz;j++)
			{
				ret=max(ret,a[j]%k);
			}
			for(int j=rq*siz;j<=r;j++)
			{
				ret=max(ret,a[j]%k);
			}
			for(int j=lq+1;j<=rq;j++)
			{
				ret=max(ret,f[j][k]);
			}
		}
		printf("%d\n",ret);
	}
	return 0;
}