[bzoj4241][分块]历史研究

5 篇文章 0 订阅

Description

IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。
日记中记录了连续N天发生的时间,大约每天发生一件。
事件有种类之分。第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大。
JOI教授决定用如下的方法分析这些日记:

  1. 选择日记中连续的一些天作为分析的时间段
  2. 事件种类t的重要度为t*(这段时间内重要度为t的事件数)
  3. 计算出所有事件种类的重要度,输出其中的最大值 现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。

Input

第一行两个空格分隔的整数N和Q,表示日记一共记录了N天,询问有Q次。
接下来一行N个空格分隔的整数X1…XN,Xi表示第i天发生的事件的种类
接下来Q行,第i行(1<=i<=Q)有两个空格分隔整数Ai和Bi,表示第i次询问的区间为[Ai,Bi]


Output

输出Q行,第i行(1<=i<=Q)一个整数,表示第i次询问的最大重要度

Sample Input

5 5

9 8 7 8 9

1 2

3 4

4 4

1 4

2 4

Sample Output

9

8

8

16

16

HINT

1<=N<=10^5

1<=Q<=10^5

1<=Xi<=10^9 (1<=i<=N)

题解

分块…
预处理块与块之间答案
然后就没了…
为什么我的常数这么大啊
为什么我还要调整块的大小啊…

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
inline LL _max(LL x,LL y){return x>y?x:y;}
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;
}
inline void write(LL x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void print(LL x){write(x);puts("");}
struct LSnode{int y,p;}w[100005];
bool cmp(LSnode n1,LSnode n2){return n1.y<n2.y;}
int cal[110000],LS[110000],fac[110000],n,m,ln;
int pos[110000],block,st[630],ed[630];
LL pr[630][630];int sum[110000];
int gg[110000],tim;
int S[630][110000];
int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	n=read();m=read();ln=n;
	for(int i=1;i<=n;i++)w[i].y=read(),w[i].p=i;
	sort(w+1,w+1+n,cmp);
	int tt=0;
	for(register int i=1;i<=n;i++)
	{
		if(w[i].y!=w[i-1].y)tt++;
		cal[w[i].p]=tt;fac[tt]=w[i].y;
	}
	block=pow(n,0.444);	
	for(register int i=1;i<=n;i++)
	{
		pos[i]=(i-1)/block+1;
		if(pos[i]!=pos[i-1])ed[pos[i-1]]=i-1,st[pos[i]]=i;
	}
	ed[pos[n]]=n;st[pos[1]]=1;block=pos[n];
	for(register int i=1;i<=block;i++)
	{
		for(register int j=1;j<=ln;j++)S[i][j]=S[i-1][j];
		for(register int j=st[i];j<=ed[i];j++)S[i][cal[j]]++;
	}
	for(register int i=1;i<=block;i++)
	{
		LL ans=0;tim++;
		for(register int j=i;j<=block;j++)
		{
			for(register int k=st[j];k<=ed[j];k++)
			{
				if(gg[cal[k]]!=tim)gg[cal[k]]=tim,sum[cal[k]]=0;
				sum[cal[k]]++,ans=_max(ans,(LL)sum[cal[k]]*fac[cal[k]]);
			}
			pr[i][j]=ans;
		}
	}
	memset(sum,0,sizeof(sum));
	while(m--)
	{
		int l=read(),r=read();
		int u=pos[l]+1,v=pos[r]-1;tim++;
		if(u>v)
		{
			LL ans=0;
			for(register int i=l;i<=r;i++)
			{
				if(gg[cal[i]]!=tim)gg[cal[i]]=tim,sum[cal[i]]=0;
				sum[cal[i]]++;ans=_max(ans,(LL)sum[cal[i]]*fac[cal[i]]);
			}
			print(ans);
		}
		else
		{
			LL ans=pr[u][v];
			for(register int i=st[v+1];i<=r;i++)
			{
				if(gg[cal[i]]!=tim)gg[cal[i]]=tim,sum[cal[i]]=S[v][cal[i]]-S[u-1][cal[i]];
				sum[cal[i]]++;ans=_max(ans,(LL)sum[cal[i]]*fac[cal[i]]);
			}
			for(register int i=l;i<=ed[u-1];i++)
			{
				if(gg[cal[i]]!=tim)gg[cal[i]]=tim,sum[cal[i]]=S[v][cal[i]]-S[u-1][cal[i]];
				sum[cal[i]]++;ans=_max(ans,(LL)sum[cal[i]]*fac[cal[i]]);
			}
			print(ans);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值