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

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(x^2)的暴力rmq,就可以预处理所有的初值。

最后对每个询问还是一样的套路,整块的就不枚,直接调用,不完整块的就暴力求解(~你的程序怎么越来越丑(man)了)

//Kaiba_Seto 20170120
//orz cjkmao
#include <math.h>
#include <stdio.h>
#include <memory.h>
#include <algorithm>
#define MaxN 100010
#define MaxS 350
#define RG register
#define inline __inline__ __attribute__((always_inline))
#define L long long
#define dmin(a,b) ((a)<(b)?(a):(b))
#define dmax(a,b) ((a)>(b)?(a):(b))

namespace io{
#define MaxBuf 1<<22
#define _getc() ((S==T&&(T=(S=B)+fread(B,1,MaxBuf,stdin),S==T))?0:*S++)
  char B[MaxBuf],*S=B,*T=B;
  template<class Type>inline void Rin(RG Type &x){
    x=0;RG int c=getchar();RG bool b=0;
    for(;c<48||c>57;c=getchar())
      if(c==45)b=1;
    for(;c>47&&c<58;c=getchar())
      x=(x<<1)+(x<<3)+c-48;
    if(b)x=-x;
  }
};

int n,_q,a[MaxN],_reflection[MaxN],block_size,block_cnt[MaxS][MaxN],belong[MaxN],lef[MaxS],rig[MaxS];
L block_rmq[MaxS][MaxS];

struct _pair{
  int first,*second;
  bool operator < (const _pair &other) const {
    return first < other.first;
  }
}c[MaxN];

inline L query(RG int x,RG int y){
  static int tmp_tim[MaxN],tmp_cnt[MaxN],T=0;
  RG int _l=belong[x],_r=belong[y];
  RG L res=block_rmq[_l+1][_r-1]; ++T;
  if(_l == _r){
    for(RG int i=x;i<=y;i++){
      if(tmp_tim[a[i]] != T){
    tmp_tim[a[i]]=T; tmp_cnt[a[i]]=0;
      }
      ++tmp_cnt[a[i]];
      res=dmax(res,(L) _reflection[a[i]] * tmp_cnt[a[i]]);
    }
    return res;
  }
  for(RG int i=x;i<=:: rig[_l];i++){
    if(tmp_tim[a[i]] != T){
      tmp_tim[a[i]]=T; tmp_cnt[a[i]]=block_cnt[_r-1][a[i]]-block_cnt[_l][a[i]];
    }
    ++tmp_cnt[a[i]];
    res=dmax(res,(L) _reflection[a[i]] * tmp_cnt[a[i]]);
  }
  for(RG int i=:: lef[_r];i<=y;i++){
    if(tmp_tim[a[i]] != T){
      tmp_tim[a[i]]=T; tmp_cnt[a[i]]=block_cnt[_r-1][a[i]]-block_cnt[_l][a[i]];
    }
    ++tmp_cnt[a[i]];
    res=dmax(res,(L) _reflection[a[i]] * tmp_cnt[a[i]]);
  }
  return res;
}

int main(){
  io::Rin(n),io::Rin(_q);
  block_size=static_cast<int>(sqrt(n)+1e-6);
  for(RG int i=1;i<=n;i++)
    io::Rin(c[i].first),c[i].second=&a[i];
  std::sort(c+1,c+1+n);
  for(RG int i=1,m=0;i<=n;i++){
    if(i==1 || c[i].first != c[i-1].first)
      _reflection[++m]=c[i].first;
    *c[i].second=m;
  }
  for(RG int i=1;i<=n;i++)
    belong[i]=(i-1)/block_size+1;
  for(RG int i=1;i<=n;i++)
    block_cnt[belong[i]][a[i]]++;
  for(RG int i=1;(i-1)*block_size+1<=n;i++)
    lef[i]=(i-1)*block_size+1,rig[i]=dmin(i*block_size,n);
  for(RG int i=1;:: lef[i];i++)
    for(RG int j=1;j<=n;j++)
      block_cnt[i][j]+=block_cnt[i-1][j];
  for(RG int i=1;lef[i];i++){
    static int tmp_cnt[MaxN]; RG L ans=0LL;
    memset(tmp_cnt,0,sizeof tmp_cnt);
    for(RG int j=lef[i];j<=n;j++){
      ++tmp_cnt[a[j]];
      ans=dmax(ans,(L) _reflection[a[j]] * tmp_cnt[a[j]]);
      if(j == rig[belong[j]])
    :: block_rmq[i][belong[j]]=ans;
    }
  }
  while(_q--){
    RG int x,y;
    io::Rin(x),io::Rin(y);
    printf("%lld\n",query(x,y));
  }
  fclose(stdin);
  return 0;
}
时间: 2024-08-06 00:10:31

[bzoj4241][历史研究] (分块)的相关文章

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

BZOJ4241 历史研究 莫队算法 堆

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

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

bzoj 4241 历史研究

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\)教授决定用如下的方法分析这些日记: 选择日记中连续

【bzoj4241】 历史研究

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

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

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

BZOJ 4236~4247 题解

BZOJ 4236 JOIOJI f[i][0..2]表示前i个字符中′J′/′O′/′I′的个数 将二元组<f[i][0]?f[i][1],f[i][1]?f[i][2]>扔进map,记录一下最早出现的时间 对于每个位置去map里面查一下即可 时间复杂度O(nlogn) #include <map> #include <cstdio> #include <cstring> #include <iostream> #include <alg

分块——优化的暴力

对于很多的题目,我们都可以找到n^2的暴力算法. 但是,当n在10000到200000之间的时候,n^2基本稳稳卡掉. 发现,这样的题目,经常还与区间有关系的时候,可以考虑分块做法. 分块,顾名思义,就是把待处理的整个大区间分成若干块. 口诀是:块外暴力,块内查表. 那么这个块的大小应该怎么分呢?? 应该是sqrt(n)大小. 证明:*&%&*^$#()&^%#$^&#$(^$%(并不会证) 自我感觉,分块很巧妙的把各种复杂度都向sqrt(N)靠近 发现很多的题,都是时间复