首页 > 其他分享 >[分块] [Luogu AT_joisc2014_c] 历史研究

[分块] [Luogu AT_joisc2014_c] 历史研究

时间:2024-05-03 18:34:24浏览次数:20  
标签:joisc2014 le 15 分块 int Luogu 1000005 样例 long

题目描述

IOI 国历史研究的第一人——JOI 教授,最近获得了一份被认为是古代 IOI 国的住民写下的日记。JOI 教授为了通过这份日记来研究古代 IOI 国的生活,开始着手调查日记中记载的事件。

日记中记录了连续 \(N\) 天发生的事件,大约每天发生一件。

事件有种类之分。第 \(i\) 天发生的事件的种类用一个整数 \(X_i\)
表示,\(X_i\) 越大,事件的规模就越大。

JOI 教授决定用如下的方法分析这些日记:

  • 选择日记中连续的几天 \([L,R]\) 作为分析的时间段;

  • 定义事件 \(A\) 的重要度 \(W_A\) 为 \(A\times T_A\),其中 \(T_A\) 为该事件在区间 \([L,R]\) 中出现的次数。

现在,您需要帮助教授求出所有事件中重要度最大的事件是哪个,并输出其重要度

注意:教授有多组询问。

输入格式

第一行两个空格分隔的整数 \(N\) 和 \(Q\),表示日记一共记录了 \(N\) 天,询问有 \(Q\) 次。

接下来一行 \(N\) 个空格分隔的整数表示每天的事件种类。

接下来 \(Q\) 行,每行给出 \(L,R\) 表示一组询问。

输出格式

输出共有 \(Q\) 行,每行一个整数,表示对应的询问的答案。

数据范围

对于 \(100\%\) 的数据,\(1\le Q,N\le 10^5\),\(1\le X\le 10^9\),\(1\le L\le R\le 10^5\)。

样例输入 #1

5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4

样例输出 #1

9
8
8
16
16

样例输入 #2

8 4
9 9 19 9 9 15 9 19
1 4
4 6
3 5
5 8

样例输出 #2

27
18
19
19

样例输入 #3

12 15
15 9 3 15 9 3 3 8 16 9 3 17
2 7
2 5
2 2
1 12
4 12
3 6
11 12
1 7
2 6
3 5
3 10
7 10
1 4
4 8
4 8

样例输出 #3

18
18
9
30
18
15
17
30
18
15
18
16
30
15
15

题解

本体的实质是找一段区间内一个数出现的次数,可以考虑用分块来做;

定义 \(sum[i][j]\) 表示前i个块中j这个数出现的位置,时间复杂度 $ O(n \sqrt n) $;

定义 $ f[i][j] $ 表示第 $ [i, j] $ 个块中的重要度最大值,时间复杂度 $ O(n \sqrt n) $;

这样,当我们查询时,如果 $ l $ 和 $ r $ 在同一个块,直接用桶数组暴力求解;

若不在同一个块,那么我们可以用 $ O(1) $ 的时间求出中间整块的重要度最大值,剩下的零散块暴力求解,最后使用sum数组找出 $ l $ 到 $ r $ 的每个数出现个数并和中间整块的重要度最大值作比较,不断更新,最后找出答案;

做此题的关键在于如何用 $ O(1) $ 的时间求出中间整块的重要度最大值,这里采用的方法是预处理(一般的时间复杂度为 $ O(n \sqrt n) $);

题目中的数据范围有时候也会给正解提供思路;

代码

#include <iostream>
#include <cstdio>
#include <map>
#include <cmath>
using namespace std;
long long a[1000005];
int cnt;
int b[1000005];
long long c[1000005];
int n, q;
int st[1000005], ed[1000005];
int belog[1000005];
int sq;
int sum[325][100005];
long long f[325][325];
long long t[1000005];
map<long long, int> mp;
long long ask(int l, int r) {
	long long ans = 0;
	if (belog[l] == belog[r]) { //直接暴力;
		for (int i = l; i <= r; i++) {
			t[b[i]]++;
		}
		for (int i = l; i <= r; i++) {
			long long o = t[b[i]] * c[b[i]];
			ans = max(ans, o);
		}
		for (int i = l; i <= r; i++) {
			t[b[i]] = 0; //使用桶数组注意最后清零;
		}
	} else {
		ans = f[belog[l] + 1][belog[r] - 1]; //中间整块的最大值;
		for (int i = l; i <= ed[belog[l]]; i++) {
			t[b[i]] = sum[belog[r] - 1][b[i]] - sum[belog[l]][b[i]]; //统计中间整块中b[i]出现的个数;
		}
		for (int i = st[belog[r]]; i <= r; i++) {
			t[b[i]] = sum[belog[r] - 1][b[i]] - sum[belog[l]][b[i]];
		}
		for (int i = l; i <= ed[belog[l]]; i++) {
			t[b[i]]++; //统计零散块中b[i]出现的个数;
		}
		for (int i = st[belog[r]]; i <= r; i++) {
			t[b[i]]++;
		}
		for (int i = l; i <= ed[belog[l]]; i++) {
			long long o = t[b[i]] * c[b[i]];
			ans = max(ans, o); //更新答案;
		}
		for (int i = st[belog[r]]; i <= r; i++) {
			long long o = t[b[i]] * c[b[i]];
			ans = max(ans, o);
		}
		for (int i = l; i <= ed[belog[l]]; i++) {
			t[b[i]] = 0; //使用桶数组注意最后清零;
		}
		for (int i = st[belog[r]]; i <= r; i++) {
			t[b[i]] = 0; //使用桶数组注意最后清零;
		}
	}
	return ans;
}
int main() {
	cin >> n >> q;
	for (int i = 1; i <= n; i++) { //需要离散化;
		cin >> a[i];
		if (mp[a[i]] == 0) {
			mp[a[i]] = ++cnt; //mp[a[i]]是a[i]离散化后对应的值;
		}
		b[i] = mp[a[i]]; //b数组是a数组离散化后对应的数组;
		c[b[i]] = a[i]; //c[i] == j代表i这个离散化后的值对应的真实值为j;
	}
	sq = sqrt(n);
	for (int i = 1; i <= sq; i++) {
		st[i] = sq * (i - 1) + 1;
		ed[i] = sq * i;
	}
	ed[sq] = n;
	for (int i = 1; i <= sq; i++) {
		for (int j = st[i]; j <= ed[i]; j++) {
			belog[j] = i;
		}
	}
	for (int i = 1; i <= sq; i++) {
		for (int j = 1; j <= ed[i]; j++) {
			sum[i][b[j]]++;
		}
	}
	for (int i = 1; i <= sq; i++) {
		for (int j = 1; j <= sq; j++) {
			long long ma = f[i][j - 1];
			for (int k = st[j]; k <= ed[j]; k++) {
				t[b[k]] = sum[j][b[k]] - sum[i - 1][b[k]];
			}
			for (int k = st[j]; k <= ed[j]; k++) {
				if (ma < t[b[k]] * c[b[k]]) ma = t[b[k]] * c[b[k]];
			}
			f[i][j] = ma;
			for (int k = st[j]; k <= ed[j]; k++) {
				t[b[k]] = 0; //使用桶数组注意最后清零;
			}
		}
	}
	int l, r;
	for (int i = 1; i <= q; i++) {
		cin >> l >> r;
		cout << ask(l, r) << endl;
	}
	return 0;
}

标签:joisc2014,le,15,分块,int,Luogu,1000005,样例,long
From: https://www.cnblogs.com/PeppaEvenPig/p/18171470

相关文章

  • 分块=-=优雅的暴力=-=中位数模版
    #include<bits/stdc++.h>//#defineintlonglong#definelllonglong#definefd(i,a,b)for(registerinti=a;i<=b;i=-~i)#definebd(i,a,b)for(registerinti=a;i>=b;i=~-i)usingnamespacestd;constintN=1e5+509,M=509,MOD=10007;intn,siz,id;......
  • CF EDU164-E-数论分块
    link:https://codeforces.com/contest/1954/problem/E有一排怪物,第\(i\)只有\(a_i\)的血,每次攻击可以选择在\(i\)处放一个技能,技能会一直向左/右以相同的\(k\)点伤害扩散,直至遇到边界或已经死亡的怪物。问最少需要放几次技能?需要对所有\(k\)回答答案。\(n,a_i\leq10^......
  • 欧拉函数 整除分块 扩展欧几里得
    欧拉函数\(\varphi(x)\)表示求出\(1\ley\lex,\gcd(x,y)=1\)的\(y\)的数量。对于一个质数\(p\),\(\varphi(p)=p-1\)\(\varphi(p^2)=p^2-\frac{p^2}{p}=p^2-p\)\(\dots\)\(\varphi(p^i)=p^i-p^{i-1}=(p-1)\cdotp^{i-1}\)......
  • 分块莫队——维护队列
    题目描述样例输入2312Q12R12Q12样例输出21题目分析首先它是一个离线莫队(在线超时QAQ)离线首先存下它所有的询问和修改,分别存,询问要存下是第几次,以便后续输出,还要存下时间是第几个命令,比较询问和修改的时间,相应的变换颜色,最后整体输出#include<bits/stdc++.h>......
  • 分块(板子)
    “优雅的暴力”——分块分块是一种暴力的优化,虽然效率比线段树、树状数组等数据结构低的多\((N+Q)\sqrt{N}\),但是更加灵活。分块的思想是把整个区间分成几个部分,对于要处理的区间包括两个部分“整块”,和区间边缘的“零散块”,“零散块”直接暴力,“整块”进行整体操作即可。......
  • 题解 LGP5397【[Ynoi2018] 天降之物】/ 第四分块
    题解P5397【[Ynoi2018]天降之物】/第四分块题目描述一个长为\(n\)的序列\(a\)。你需要实现\(m\)个操作,操作有两种:把序列中所有值为\(x\)的数的值变成\(y\)。找出一个位置\(i\)满足\(a_i=x\),找出一个位置\(j\)满足\(a_j=y\),使得\(|i-j|\)最小,并输出\(|i-......
  • 蒲公英(分块)
    [Violet]蒲公英题目背景亲爱的哥哥:你在那个城市里面过得好吗?我在家里面最近很开心呢。昨天晚上奶奶给我讲了那个叫「绝望」的大坏蛋的故事的说!它把人们的房子和田地搞坏,还有好多小朋友也被它杀掉了。我觉得把那么可怕的怪物召唤出来的那个坏蛋也很坏呢。不过奶奶说他是很难受......
  • 树上点差分的经典应用 LuoguP3258松鼠的新家
    树上点差分的核心就是如何避免重复,即正确的运用差分数组例如a,b点路径上点权值加1,则把a,b路径找到,并找到其LCA,此时可以把a到根,b到根这两条路径看出两条链,把每条链看出我们熟悉的顺序差分结构.以其中一条链为例子,把a当成数组的起点,根当成数组的末尾,进行差分,显然有C[a]+......
  • 分块板子
    预处理voidinit(){clean();scanf("%lld",&n);for(i=1;i<=n;i++)scanf("%lld",&a[i]);sq=sqrt(n);for(i=1;i<=sq;i++){st[i]=n/sq*(i-1)+1;ed[i]=n/sq*i;}ed[sq]=n;for(i=1;i<......
  • 分块--解决区间问题
    什么时候用分块?当你发现正常数据结构无法解决的时候(比如维度特别多,很不方便或者炸空间),或者复杂到要3个$log$以上才能解决时。(主要还是得看数据范围,分块的做法一般都是$O(\sqrt{n})$及以上的如何分块?定一个块长$B$,整个序列就会被分成$\floor{n/B}$块,对于末尾的不......