基础莫队
适用于求区间内不同数的个数问题
排序方式
如果当前两个区间左端点在同一个奇数块,那么按右端点递减排序,否则按右端点递增排序
分块大小和时间复杂度
将待查询区间分块排序 块大小为 n \sqrt{n} n
最坏时间复杂度 O ( n n ) O(n\sqrt{n}) O(nn)
模板代码
#include<bits/stdc++.h>
#include<unordered_map>
#pragma GCC optimize(2)
// #define int long long
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))
#define debug(x,y) cerr << (x) << " == " << (y) << endl;
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
//template<typename T> T qmi(T a, T b = mod - 2, T p = mod) { T res = 1; b %= (p - 1 == 0 ? p : p - 1); while (b) { if (b & 1) { res = (LL)res * a % p; }b >>= 1; a = (LL)a * a % p; }return res % mod; }
const int N = 50010, M = 200010, S = 1000010;
int n, m;
int a[N], cnt[S], ans[M];
int block;
struct Node {
int id, l, r;
}node[M];
bool cmp(const Node& a, const Node& b) {
// 如果当前两个区间左端点在同一个奇数块,那么按右端点递减排序,否则按右端点递增排序
int l = a.l / block, r = b.l / block;
return l ^ r ? l < r : (l & 1 ? a.r < b.r : a.r > b.r);
}
void add(int x, int& res) {
cnt[x]++;
if (cnt[x] == 1)res++;
}
void del(int x, int& res) {
cnt[x]--;
if (!cnt[x])res--;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
cin >> m;
for (int i = 1; i <= m; ++i) {
int l, r; scanf("%d%d", &l, &r);
node[i] = { i,l,r };
}
block = n / sqrt(n);
sort(node + 1, node + 1 + m, cmp);
int i = 0, j = 1;
int res = 0;
for (int k = 1; k <= m; ++k) {// 对排序后的查询进行计算
int l = node[k].l;
int r = node[k].r;
// 将add del函数 通过运算优先级优化成常数
while (i < r)res += !cnt[a[++i]]++;
while (i > r)res -= !--cnt[a[i--]];
while (j < l)res -= !--cnt[a[j++]];
while (j > l)res += !cnt[a[--j]]++;
ans[node[k].id] = res;
}
for (int i = 1; i <= m; ++i) {
printf("%d\n", ans[i]);
}
}
signed main() {
// int _; cin >> _;
// while (_--)
solve();
return 0;
}
HH的项链
题意
HH 有一串由各种漂亮的贝壳组成的项链。
HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。
HH 不断地收集新的贝壳,因此他的项链变得越来越长。
有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?
这个问题很难回答,因为项链实在是太长了。
于是,他只好求助睿智的你,来解决这个问题。
思路
莫队模板题
数据范围
1 ≤ N ≤ 50000 1≤N≤50000 1≤N≤50000
1 ≤ M ≤ 2 × 1 0 5 1≤M≤2×10^5 1≤M≤2×105
1 ≤ L ≤ R ≤ N 1≤L≤R≤N 1≤L≤R≤N
代码
#include<bits/stdc++.h>
#include<unordered_map>
#pragma GCC optimize(2)
// #define int long long
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))
#define debug(x,y) cerr << (x) << " == " << (y) << endl;
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
//template<typename T> T qmi(T a, T b = mod - 2, T p = mod) { T res = 1; b %= (p - 1 == 0 ? p : p - 1); while (b) { if (b & 1) { res = (LL)res * a % p; }b >>= 1; a = (LL)a * a % p; }return res % mod; }
const int N = 50010, M = 200010, S = 1000010;
int n, m;
int a[N], cnt[S], ans[M];
int block;
struct Node {
int id, l, r;
}node[M];
bool cmp(const Node& a, const Node& b) {
// 如果当前两个区间左端点在同一个奇数块,那么按右端点递减排序,否则按右端点递增排序
int l = a.l / block, r = b.l / block;
return l ^ r ? l < r : (l & 1 ? a.r < b.r : a.r > b.r);
}
void add(int x, int& res) {
cnt[x]++;
if (cnt[x] == 1)res++;
}
void del(int x, int& res) {
cnt[x]--;
if (!cnt[x])res--;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
cin >> m;
for (int i = 1; i <= m; ++i) {
int l, r; scanf("%d%d", &l, &r);
node[i] = { i,l,r };
}
block = n / sqrt(n);
sort(node + 1, node + 1 + m, cmp);
int i = 0, j = 1;
int res = 0;
for (int k = 1; k <= m; ++k) {// 对排序后的查询进行计算
int l = node[k].l;
int r = node[k].r;
// 将add del函数 通过运算优先级优化成常数
while (i < r)res += !cnt[a[++i]]++;
while (i > r)res -= !--cnt[a[i--]];
while (j < l)res -= !--cnt[a[j++]];
while (j > l)res += !cnt[a[--j]]++;
ans[node[k].id] = res;
}
for (int i = 1; i <= m; ++i) {
printf("%d\n", ans[i]);
}
}
signed main() {
// int _; cin >> _;
// while (_--)
solve();
return 0;
}
Different Intergers
来源:牛客2018多校
题意
给定一个长度为 n 的数组 a,和 m 个询问,每次询问给定一个 [l,r] 求出 a 1 , a 2 , … a i , a j , a j + 1 , … , a n a_1,a_2,\dots a_i,a_j,a_{j + 1},\dots,a_n a1,a2,…ai,aj,aj+1,…,an 中不同的数的数量
数据范围
1 ≤ n , q ≤ 1 0 5 1 ≤ n, q ≤ 10^5 1≤n,q≤105
1 ≤ a i ≤ n 1 ≤ a_i ≤ n 1≤ai≤n
1 ≤ l i , r i ≤ n 1 ≤ l_i, r_i ≤ n 1≤li,ri≤n
思路
与求某区间内不同数的思路相同,先用莫队思想把查询区间排序,与上题(HH的项链)不同的是,将 l 初始为 0,r初始为 n + 1,最初包括整个区间。这样每次用 l,r与当前查询的 lr比较。
例如,如果 e [ i ] . r < r e[i].r < r e[i].r<r 那么 r递减到 e [ i ] . r e[i].r e[i].r 就会把 $r 到 到 到 e[i].r$ 中所有的数加进去,因为 r 是从 n + 1 即整个区间的右端点开始的,那么当前加进去的数就是 e [ i ] . r e[i].r e[i].r 到 n 的所有数,满足了题目的要求。
l l l 从0 开始类似。
相应的add
del
操作也要进行更改,因为 r 变大或者 l 变小时,区间里的数是减少的。r 变小或者 l 变大时,区间里的数是增加的。
代码
#include<bits/stdc++.h>
#include<unordered_map>
// #define int long long
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))
#define debug(x,y) cerr << (x) << " == " << (y) << endl;
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
template<typename T> T qmi(T a, T b = mod - 2, T p = mod) { T res = 1; b %= (p - 1 == 0 ? p : p - 1); while (b) { if (b & 1) { res = (LL)res * a % p; }b >>= 1; a = (LL)a * a % p; }return res % mod; }
const int N = 1e5 + 10;
int n, m;
int a[N], ans[N], cnt[N];
int block;
struct Node {
int id, l, r;
}node[N];
bool cmp(const Node& a, const Node& b) {
if (a.l / block == b.l / block)return a.r < b.r;
return a.l / block < b.l / block;
}
void add(int x, int& res) {
cnt[x] ++;
if (cnt[x] == 1)res++;
}
void del(int x, int& res) {
cnt[x]--;
if (!cnt[x])res--;
}
void solve() {
while (~scanf("%d%d", &n, &m)) {
memset(cnt, 0, sizeof cnt);
for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
block = sqrt(n);
for (int i = 1; i <= m; ++i) {
scanf("%d%d", &node[i].l, &node[i].r);
node[i].id = i;
}
sort(node + 1, node + 1 + m, cmp);
int l = 0, r = n + 1;
int res = 0;
for (int i = 1; i <= m; ++i) {
int id = node[i].id;
while (r < node[i].r)del(a[r++], res);
while (r > node[i].r)add(a[--r], res);
while (l < node[i].l)add(a[++l], res);
while (l > node[i].l)del(a[l--], res);
ans[id] = res;
}
for (int i = 1; i <= m; ++i)printf("%d\n", ans[i]);
}
}
signed main() {
// int _; cin >> _;
// while (_--)
solve();
return 0;
}
带修莫队
适用于查询区间内不同数的个数,加修改区间内某个数
排序方式:
bool cmp(Query& a, Query& b) {
return (a.l / block) ^ (b.l / block) ? (a.l / block < b.l / block) : (a.r / block) ^ (b.r / block) ? (a.r / block < b.r / block) : (a.t < b.t);
}
如果左端点块号奇偶性不同,按照块号递增排序
否则 如果右端点奇偶性不同,按照块号递增排序
否则 按照时间戳递增排序
分块大小和时间复杂度
当块的大小为 n 2 3 n^{\frac{2}{3}} n32 时,理论复杂度最优为 O ( n 5 3 ) O(n^{\frac{5}{3}}) O(n35)
模板代码
#include<bits/stdc++.h>
#include<unordered_map>
// #define int long long
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))
#define debug(x,y) cerr << (x) << " == " << (y) << endl;
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
// template<typename T> T qmi(T a, T b = mod - 2, T p = mod) { T res = 1; b %= (p - 1 == 0 ? p : p - 1); while (b) { if (b & 1) { res = (LL)res * a % p; }b >>= 1; a = (LL)a * a % p; }return res % mod; }
const int N = 2e5 + 10, S = 1e6 + 10;
int n, m;
int cnt[S]; // 记录每个数出现的次数
int a[N]; // 原数组
int mq, mc; // 询问和修改的次数
int block; // 分块的大小
int ans[N]; // 记录每个询问的答案
struct Query { // 询问操作结构体
int id, l, r, t;
// id 表示询问的先后顺序
// t 表示 时间戳 即离它最近的修改操作的编号
}q[N];
struct Modify { // 修改操作结构体
// 将 l 位置上的数修改成 r
int l, r;
}c[N];
bool cmp(Query& a, Query& b) { // 排序比较函数
return (a.l / block) ^ (b.l / block) ? (a.l / block < b.l / block) : (a.r / block) ^ (b.r / block) ? (a.r / block < b.r / block) : (a.t < b.t);
}
void del(int x,int &res){ // 删除操作
cnt[x]--;
if (!cnt[x])res--;
}
void add(int x, int& res) { // 增加操作
if (!cnt[x])res++;
cnt[x]++;
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
for (int i = 1; i <= m; ++i) {
char op[2];
int l, r;
scanf("%s%d%d", op, &l, &r);
//离线存储所有询问和修改操作
if (op[0] == 'Q') q[++mq] = { mq,l,r,mc };
else c[++mc] = { l,r };
}
block = cbrt((double)n * n); // 计算分块的大小
sort(q + 1, q + 1 + mq, cmp);
int l = 1, r = 0, t = 0, res = 0;
for (int k = 1; k <= m; ++k) {
int id = q[k].id;
// 水平区间上进行移动
while (l < q[k].l)res -= !--cnt[a[l++]];
while (l > q[k].l)res += !cnt[a[--l]]++;
while (r < q[k].r)res += !cnt[a[++r]]++;
while (r > q[k].r)res -= !--cnt[a[r--]];
// 时间戳上进行移动
// t 表示 1-t 的修改操作已经执行
while (t < q[k].t) {
// 需要将时间戳往后移动
t++;
// 如果需要修改的数在当前 [l, r] 区间内
// 将被修改的数删除,将修改成的数加入
if (c[t].l >= l && c[t].l <= r) {
res -= !--cnt[a[c[t].l]];
res += !cnt[c[t].r]++;
}
// 每次操作将两个数 swap 一下,就不用存当前数被修改成了哪个数
swap(a[c[t].l], c[t].r);
}
while (t > q[k].t) {
// 因为 t 较大 所以要先把当前的 t 删除 再 t--
if (c[t].l >= l && c[t].l <= r) {
res -= !--cnt[a[c[t].l]];
res += !cnt[c[t].r]++;
}
swap(a[c[t].l], c[t].r);
t--;
}
ans[id] = res;
}
for (int i = 1; i <= mq; ++i)printf("%d\n", ans[i]);
}
signed main() {
// int _; cin >> _;
// while (_--)
solve();
return 0;
}
Acwing 2521. 数颜色
墨墨购买了一套 N N N 支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。
墨墨会像你发布如下指令:
Q L R
代表询问你从第 L L L 支画笔到第 R R R 支画笔中共有几种不同颜色的画笔。R P Col
把第 P P P 支画笔替换为颜色 C o l Col Col。
为了满足墨墨的要求,你知道你需要干什么了吗?
输入格式
第 1 1 1 行两个整数 N , M N,M N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。
第 2 2 2 行 N N N 个整数,分别代表初始画笔排中第 i 支画笔的颜色。
第 3 3 3 行到第 2 + M 2+M 2+M 行,每行分别代表墨墨会做的一件事情,格式见题干部分。
输出格式
对于每一个 Query
的询问,你需要在对应的行中给出一个数字,代表第
L
L
L 支画笔到第 R 支画笔中共有几种不同颜色的画笔。
数据范围
1
≤
N
,
M
≤
10000
1≤N,M≤10000
1≤N,M≤10000
修改操作不多于
1000
1000
1000 次,
所有的输入数据中出现的所有整数均大于等于
1
1
1 且不超过
1
0
6
10^6
106
代码
#include<bits/stdc++.h>
#include<unordered_map>
// #define int long long
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))
#define debug(x,y) cerr << (x) << " == " << (y) << endl;
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
// template<typename T> T qmi(T a, T b = mod - 2, T p = mod) { T res = 1; b %= (p - 1 == 0 ? p : p - 1); while (b) { if (b & 1) { res = (LL)res * a % p; }b >>= 1; a = (LL)a * a % p; }return res % mod; }
const int N = 2e5 + 10, S = 1e6 + 10;
int n, m;
int cnt[S];
int a[N];
int mq, mc;
int block;
int ans[N];
struct Query {
int id, l, r, t;
}q[N];
struct Modify {
int l, r;
}c[N];
bool cmp(Query& a, Query& b) {
return (a.l / block) ^ (b.l / block) ? (a.l / block < b.l / block) : (a.r / block) ^ (b.r / block) ? (a.r / block < b.r / block) : (a.t < b.t);
}
void del(int x,int &res){
cnt[x]--;
if (!cnt[x])res--;
}
void add(int x, int& res) {
if (!cnt[x])res++;
cnt[x]++;
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
for (int i = 1; i <= m; ++i) {
char op[2];
int l, r;
scanf("%s%d%d", op, &l, &r);
if (op[0] == 'Q') q[++mq] = { mq,l,r,mc };
else c[++mc] = { l,r };
}
block = cbrt((double)n * n);
sort(q + 1, q + 1 + mq, cmp);
int l = 1, r = 0, t = 0, res = 0;
for (int k = 1; k <= m; ++k) {
int id = q[k].id;
while (l < q[k].l)res -= !--cnt[a[l++]];
while (l > q[k].l)res += !cnt[a[--l]]++;
while (r < q[k].r)res += !cnt[a[++r]]++;
while (r > q[k].r)res -= !--cnt[a[r--]];
while (t < q[k].t) {
t++;
if (c[t].l >= l && c[t].l <= r) {
res -= !--cnt[a[c[t].l]];
res += !cnt[c[t].r]++;
}
swap(a[c[t].l], c[t].r);
}
while (t > q[k].t) {
if (c[t].l >= l && c[t].l <= r) {
res -= !--cnt[a[c[t].l]];
res += !cnt[c[t].r]++;
}
swap(a[c[t].l], c[t].r);
t--;
}
ans[id] = res;
}
for (int i = 1; i <= mq; ++i)printf("%d\n", ans[i]);
}
signed main() {
// int _; cin >> _;
// while (_--)
solve();
return 0;
}
回滚莫队
适用于加入一个数很方便,但是删除一个数很困难的问题
①暴力求右端点在块内的情况
②右端点不在块内时 ,cnt[] res 维护区间的块外部分
排序方式
因为需要保持同一个块内的询问右端点递增,所以不能使用基础莫队的优化版排序方式,而应该:
首先按照分块的编号递增排序,然后块内按照右端点递增排序
分块大小和时间复杂度
块的大小为 n \sqrt{n} n 时间复杂度为 O ( n n ) O(n\sqrt{n}) O(nn)
模板代码
#include<bits/stdc++.h>
#include<unordered_map>
// #define int long long
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))
#define debug(x,y) cerr << (x) << " == " << (y) << endl;
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
// template<typename T> T qmi(T a, T b = mod - 2, T p = mod) { T res = 1; b %= (p - 1 == 0 ? p : p - 1); while (b) { if (b & 1) { res = (LL)res * a % p; }b >>= 1; a = (LL)a * a % p; }return res % mod; }
const int N = 1e5 + 10, S = 1e6 + 10;
int n, m;
int cnt[N], a[N];
LL ans[N];
vector<int>nums;
int block;
struct Query {
int id, l, r;
}q[N];
bool cmp(const Query& a, const Query& b) {
int l = a.l / block, r = b.l / block;
if (l != r)return l < r;
return a.r < b.r;
}
void add(int x, LL& res) {
cnt[x]++;
res = max(res, (LL)cnt[x] * nums[x]);
}
void solve() {
scanf("%d%d", &n, &m);
block = sqrt(n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
nums.push_back(a[i]);
}
// 离散化
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
for (int i = 1; i <= n; ++i)
a[i] = lower_bound(nums.begin(), nums.end(), a[i]) - nums.begin();
// 离线存储询问
for (int i = 0; i < m; ++i) {
int l, r; scanf("%d%d", &l, &r);
q[i] = { i,l,r };
}
sort(q, q + m, cmp);
// x y 为询问的编号
for (int x = 0; x < m;) {
// 求出当前块包含了哪些询问 [x,y)
int y = x;
while (y < m && q[x].l / block == q[y].l / block)y++;
// right 表示当前块的右端点
int right = q[x].l / block * block + block - 1;
// 如果当前询问的右端点在块内 那么就暴力求
while (x < y && q[x].r <= right) {
LL res = 0;
int id = q[x].id, l = q[x].l, r = q[x].r;
for (int k = l; k <= r; ++k)add(a[k], res);
ans[id] = res;
// 每次求完,要将cnt数组清空
for (int k = l; k <= r; ++k)cnt[a[k]]--;
x++;
}
// 如果当前询问的右端点在块外
LL res = 0;
int i = right, j = right + 1; // 左右指针
while (x < y) {
int id = q[x].id, l = q[x].l, r = q[x].r;
// 因为按右端点排序 所以 i 只会往右递增 不会对 i 指向的数进行删除操作
while (i < r)add(a[++i], res);
// 对于每个查询 都让 j 从块的右端点向左移动
// 先将答案(分块右端点 --- 右指针)备份
LL backup = res;
// 左指针 j 往左扫到查询的左端点,看能否更新res
while (j > l)add(a[--j], res);
ans[id] = res;
// 每次将 通过 j 加入的数删除
while (j < right + 1)cnt[a[j++]]--;
// 恢复 res 的备份
res = backup;
x++;
}
// 对每个块处理完后 cnt要清零
memset(cnt, 0, sizeof cnt);
}
for (int i = 0; i < m; ++i)printf("%lld\n", ans[i]);
}
signed main() {
// int _; cin >> _;
// while (_--)
solve();
return 0;
}
Acwing2523. 历史研究
I O I IOI IOI 国历史研究的第一人—— J O I JOI JOI 教授,最近获得了一份被认为是古代 I O I IOI IOI 国的住民写下的日记。
J O I JOI JOI 教授为了通过这份日记来研究古代 I O I IOI IOI 国的生活,开始着手调查日记中记载的事件。
日记中记录了连续 N N N 天发生的时间,大约每天发生一件。
事件有种类之分。第 i i i 天 ( 1 ≤ i ≤ N ) (1≤i≤N) (1≤i≤N) 发生的事件的种类用一个整数 X i X_i Xi 表示, X i X_i Xi 越大,事件的规模就越大。
J O I JOI JOI 教授决定用如下的方法分析这些日记:
- 选择日记中连续的一些天作为分析的时间段
- 事件种类 t t t 的重要度为 t t t × (这段时间内重要度为 t t t 的事件数)
- 计算出所有事件种类的重要度,输出其中的最大值
现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。
输入格式
第一行两个空格分隔的整数 N N N 和 Q Q Q,表示日记一共记录了 N N N 天,询问有 Q Q Q 次。
接下来一行 N N N 个空格分隔的整数 X 1 … X N X_1…X_N X1…XN, X i X_i Xi 表示第 i i i 天发生的事件的种类。
接下来 Q Q Q 行,第 i i i 行 ( 1 ≤ i ≤ Q ) (1≤i≤Q) (1≤i≤Q) 有两个空格分隔整数 A i A_i Ai 和 B i B_i Bi,表示第 i i i 次询问的区间为 [ A i , B i ] [A_i,B_i] [Ai,Bi]。
输出格式
输出 Q Q Q 行,第 i i i 行 ( 1 ≤ i ≤ Q ) (1≤i≤Q) (1≤i≤Q) 一个整数,表示第 i i i 次询问的最大重要度。
数据范围
1
≤
N
≤
1
0
5
1≤N≤10^5
1≤N≤105
1
≤
Q
≤
1
0
5
1≤Q≤10^5
1≤Q≤105
1
≤
X
i
≤
1
0
9
1≤X_i≤10^9
1≤Xi≤109
代码
#include<bits/stdc++.h>
#include<unordered_map>
// #define int long long
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))
#define debug(x,y) cerr << (x) << " == " << (y) << endl;
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
// template<typename T> T qmi(T a, T b = mod - 2, T p = mod) { T res = 1; b %= (p - 1 == 0 ? p : p - 1); while (b) { if (b & 1) { res = (LL)res * a % p; }b >>= 1; a = (LL)a * a % p; }return res % mod; }
const int N = 1e5 + 10, S = 1e6 + 10;
int n, m;
int cnt[N], a[N];
LL ans[N];
vector<int>nums;
int block;
struct Query {
int id, l, r;
}q[N];
bool cmp(const Query& a, const Query& b) {
int l = a.l / block, r = b.l / block;
if (l != r)return l < r;
return a.r < b.r;
}
void add(int x, LL& res) {
cnt[x]++;
res = max(res, (LL)cnt[x] * nums[x]);
}
void solve() {
scanf("%d%d", &n, &m);
block = sqrt(n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
nums.push_back(a[i]);
}
// 离散化
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
for (int i = 1; i <= n; ++i)
a[i] = lower_bound(nums.begin(), nums.end(), a[i]) - nums.begin();
// 离线存储询问
for (int i = 0; i < m; ++i) {
int l, r; scanf("%d%d", &l, &r);
q[i] = { i,l,r };
}
sort(q, q + m, cmp);
// x y 为询问的编号
for (int x = 0; x < m;) {
// 求出当前块包含了哪些询问 [x,y)
int y = x;
while (y < m && q[x].l / block == q[y].l / block)y++;
// right 表示当前块的右端点
int right = q[x].l / block * block + block - 1;
// 如果当前询问的右端点在块内 那么就暴力求
while (x < y && q[x].r <= right) {
LL res = 0;
int id = q[x].id, l = q[x].l, r = q[x].r;
for (int k = l; k <= r; ++k)add(a[k], res);
ans[id] = res;
// 每次求完,要将cnt数组清空
for (int k = l; k <= r; ++k)cnt[a[k]]--;
x++;
}
// 如果当前询问的右端点在块外
LL res = 0;
int i = right, j = right + 1; // 左右指针
while (x < y) {
int id = q[x].id, l = q[x].l, r = q[x].r;
// 因为按右端点排序 所以 i 只会往右递增 不会对 i 指向的数进行删除操作
while (i < r)add(a[++i], res);
// 对于每个查询 都让 j 从块的右端点向左移动
// 先将答案(分块右端点 --- 右指针)备份
LL backup = res;
// 左指针 j 往左扫到查询的左端点,看能否更新res
while (j > l)add(a[--j], res);
ans[id] = res;
// 每次将 通过 j 加入的数删除
while (j < right + 1)cnt[a[j++]]--;
// 恢复 res 的备份
res = backup;
x++;
}
// 对每个块处理完后 cnt要清零
memset(cnt, 0, sizeof cnt);
}
for (int i = 0; i < m; ++i)printf("%lld\n", ans[i]);
}
signed main() {
// int _; cin >> _;
// while (_--)
solve();
return 0;
}