BZOJ 4241: 歷史研究

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)

Source

JOI 2013~2014 春季training合宿 競技1 By PoPoQQQ

Solution

  • 題意 : 多次詢問區間中的 每個數*其出現次數 的最大值。

  • 我們可以離散化後做莫隊,但是普通莫隊是不可行的,因爲刪去最大值我們並不知道次大值。

  • 這是我們需要稍作修改——回滾莫隊!

  • 同樣將詢問區間按左端點所屬塊爲第一關鍵字、右端點爲第二關鍵字從小到大排,

  • 每次我們將左指針置於塊的最右端,每次重新移動即可。

  • 若詢問的區間在同一塊內就 O(n)O(\sqrt n) 暴力統計。

  • 總時間複雜度是 O(nn)O(n\sqrt n) 的。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=1e5+5;
struct data
{
	int l,r,id;
}c[N];
LL sum;
int a[N],b[N],bel[N],t[N],cnt[N];
LL ans[N];
inline int read()
{
	int X=0,w=0; char ch=0;
	while(!isdigit(ch)) w|=ch=='-',ch=getchar();
	while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
	return w?-X:X;
}
inline bool cmp(data x,data y)
{
	return bel[x.l]<bel[y.l] || bel[x.l]==bel[y.l] && x.r<y.r;
}
inline LL max(LL x,LL y)
{
	return x>y?x:y;
}
inline int min(int x,int y)
{
	return x<y?x:y;
}
inline void add(int x)
{
	cnt[a[x]]++;
	sum=max(sum,(LL)cnt[a[x]]*b[a[x]]);
}
inline void del(int x)
{
	cnt[a[x]]--;
}
int main()
{
	int n=read(),q=read();
	int size=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		a[i]=b[i]=read();
		bel[i]=(i-1)/size+1;
	}
	sort(b+1,b+1+n);
	b[0]=unique(b+1,b+1+n)-(b+1);
	for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+b[0],a[i])-b;
	for(int i=1;i<=q;i++) c[i].l=read(),c[i].r=read(),c[i].id=i;
	sort(c+1,c+1+q,cmp);
	for(int i=1,j=1;i<=n;i+=size)
	{
		sum=0;
		int up=min(n,i+size-1);
		int l=up+1,r=up;
		memset(cnt,0,sizeof(cnt));
		while(j<=q && bel[i]==bel[c[j].l])
		{
			if(bel[c[j].l]==bel[c[j].r])
			{
				LL val=0;
				for(int k=c[j].l;k<=c[j].r;k++) t[a[k]]=0;
				for(int k=c[j].l;k<=c[j].r;k++)
				{
					t[a[k]]++;
					val=max(val,(LL)t[a[k]]*b[a[k]]);
				}
				ans[c[j].id]=val;
				j++;
				continue;
			}
			while(r<c[j].r) add(++r);
			LL val=sum;
			while(l>c[j].l) add(--l);
			ans[c[j].id]=sum;
			while(l<=up) del(l++);
			sum=val;
			j++;
		}
	}
	for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章