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

  这道题刚开始时没有思路......后来发现好像和区间众数的方法有点像......我也是醉了

  好了,我们考虑如何做这道题。首先,我们可以把区间分块。然后,我们可以用n^1.5的复杂度求出f[i][j],表示取第i块到第j块中所有元素的答案。

  然后,我们考虑如何得到区间[l,r]的答案。如果l和r在同一块,那么显然扫一遍就可以了。否则,我们可以先把[l,r]覆盖的完整的块的答案统计一下,再统计一下边角余料中答案最大的数。显然答案一定在这三者中。

  但是,统计边角余料的答案并不好做。于是,我们可以再来一个数组cnt[i][j],表示前i个块中第j号元素出现了多少次(注意先要离散化),然后我们就可以O(1)地统计完整的块中每种元素出现的个数了。取一下max就可以了。

  下面是代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
 7 #define maxn 100010
 8 #define kuai 501
 9
10 using namespace std;
11 typedef long long llg;
12
13 int n,m,N,ln,L[kuai],R[kuai],cnt[kuai][maxn];
14 int a[maxn],b[maxn],lb,c[maxn],ci[maxn],be[maxn];
15 llg f[kuai][kuai],ans;
16
17 int getint(){
18     int w=0;bool q=0;
19     char c=getchar();
20     while((c>‘9‘||c<‘0‘)&&c!=‘-‘) c=getchar();
21     if(c==‘-‘) q=1,c=getchar();
22     while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar();
23     return q?-w:w;
24 }
25
26 int main(){
27     n=getint();m=getint();
28     for(int i=1;i<=n;i++) a[i]=b[i]=getint();
29     sort(b+1,b+n+1); lb=unique(b+1,b+n+1)-b-1;
30     for(int i=1;i<=n;i++){
31         int l=1,r=lb,mid;
32         while(l!=r){
33             mid=l+r>>1;
34             if(a[i]<=b[mid]) r=mid;
35             else l=mid+1;
36         }
37         c[i]=l;
38     }
39     N=sqrt(n); ln=n/N; if(n%N) ln++;
40     for(int i=1;i<ln;i++) L[i]=R[i-1]+1,R[i]=N*i;
41     L[ln]=R[ln-1]+1; R[ln]=n;
42     for(int i=1,r;i<=ln;i++){
43         ans=0; r=L[i]-1;
44         for(int j=L[i];j<=R[i];j++) cnt[i][c[j]]++,be[j]=i;
45         for(int j=1;j<=ln;j++){
46             while(r<R[j]){
47                 r++; ci[c[r]]++;
48                 ans=max(ans,(llg)(ci[c[r]])*(llg)a[r]);
49             }
50             f[i][j]=ans;
51         }
52         for(int j=1;j<=n;j++) cnt[i][j]+=cnt[i-1][j];
53         for(int j=1;j<=lb;j++) ci[j]=0;
54     }
55     while(m--){
56         int l=getint(),r=getint(); ans=0;
57         if(be[l]==be[r]){
58             for(int i=l;i<=r;i++)
59                 ans=max(ans,(llg)(++ci[c[i]])*(llg)a[i]);
60             for(int i=l;i<=r;i++) ci[c[i]]--;
61         }
62         else{
63             ans=f[be[l]+1][be[r]-1];
64             for(int i=l;i<=R[be[l]];i++)
65                 ans=max(ans,(llg)((++ci[c[i]])+cnt[be[r]-1][c[i]]-cnt[be[l]][c[i]])*(llg)a[i]);
66             for(int i=L[be[r]];i<=r;i++)
67                 ans=max(ans,(llg)((++ci[c[i]])+cnt[be[r]-1][c[i]]-cnt[be[l]][c[i]])*(llg)a[i]);
68             for(int i=l;i<=R[be[l]];i++) ci[c[i]]--;
69             for(int i=L[be[r]];i<=r;i++) ci[c[i]]--;
70         }
71         printf("%lld\n",ans);
72     }
73     return 0;
74 }
时间: 2024-10-10 16:50:19

bzoj 4241 历史研究的相关文章

BZOJ4241 历史研究 莫队算法 堆

欢迎访问~原文出处--博客园-zhouzhendong&AK 去博客园看该题解 题目 Description IOI国历史研究的第一人--JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. 日记中记录了连续N天发生的时间,大约每天发生一件. 事件有种类之分.第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大. JOI教授决定用如下的方法分析这些日记: 1.

[bzoj4241][历史研究] (分块)

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

【AT1219】历史研究

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

bzoj 4241【历史的研究】

这道题是求一个区间最值的,而且并没有强制在线,空间也比较常规.所以这题你写分块或莫队都可以. 但是猛地发现,求最值没法儿删除啊!莫队的删除操作该怎么办呢?想一下,你对询问排序之后,当前你左指针在询问左端点的左边,这时候你需要把经过的数的影响删去,但最大值并不好维护啊.所以普通的莫队思路无法解决此类问题. 因此,我们引入了回滚莫队.回滚莫队,顾名思义,就是滚来滚去的莫队算法.你写写发现它真的是在滚来滚去.我们调整指针的时候,从左向右无法处理,那我们可以每次先让左指针尽可能靠右,把从左向右转化为从右

bzoj4241 历史研究

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4241 [题解] 和作诗相似. f[i,j]表示块i到块j的答案. g[i,j]表示1...i块中j出现次数. 那么分块直接做即可. 复杂度O(n根号n) 跑的好慢啊.. # include <vector> # include <stdio.h> # include <string.h> # include <algorithm> // # inclu

【bzoj4241】 历史研究

http://www.lydsy.com/JudgeOnline/problem.php?id=4241 (题目链接) 看到题目就联想到了[bzoj2809] Apio2012—dispatching.想了想权值分块+莫队,发现不好维护块内最值,又看了看80s的时间,于是怒水一发线段树+莫队,结果先WA后TLE,不断TLE,无论怎么改常数都不行,难道nlogn*sqrt(n)就是过不了吗!!不爽,蒯个题解,再见! 题意:求区间加权众数. solution  貌似是分块,离散化之后,用mx[i][

bzoj4241 历史研究——分块

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4241 就是分块,预处理出从第 i 块到 j 位置的答案,以及从第 i 块到最后位置间每个数出现的次数: 然后块内统计.块外暴力即可. 代码如下: #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using na

BZOJ 1968 约数研究

其实打个表就会发现,这个玩意儿是积性的,然后很happy的搞了一下. 不,不是这样. 考虑每个约数对答案的贡献,不难发现:约数i的贡献为n/i. 加之即可. #include<iostream>#include<cstdio>using namespace std;int n,sum=0;int main(){ scanf("%d",&n); for (int i=1;i<=n;i++) sum=sum+n/i; printf("%d&q

习题:历史研究(回滚莫队)

题目 传送门 思路 很版的一道回滚莫队的题 我们如果用普通的莫队,我们发现最难维护的是最大值, 因为你无法预测缩减时最大值的变化,还要带一个线段树或者什么来维护 时间复杂度为\(O(n*log_n*\sqrt n)\) 但是我们想,我们如果已知一个莫队的左端点和右端点以及它的最大值 那么这个莫队向外拓展我们是很容易维护的 之后如果下一个操作也是向外拓展就向外拓展,如果是内缩, 我们就将这个莫队还原成为我们最开始已知的样子,在进行拓展 这也就是回滚莫队的主要思想,这道题也是如此 时间复杂度依然也是