数列分块入门 1
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法和单点查询
模板题
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<ctime>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int INF=0x3f3f3f3f;
ll num[100005];
ll pos[100005]; ///每个数属于那个一个块
ll lazy[505]; ///每个区间的lazy
ll cnt; ///一个块块的数量
void block(ll n)
{
memset(lazy,0,sizeof(lazy));
cnt=(ll)sqrt(n);
for(int i=1;i<=n;i++)
pos[i]=(i+cnt-1)/cnt;
}
void add(ll l,ll r,ll x)
{
for(int i=l;i<=min(pos[l]*cnt,r);i++)
num[i]+=x;
if(pos[l]!=pos[r])
{
for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
num[i]+=x;
}
for(int i=pos[l]+1;i<=pos[r]-1;i++)
lazy[i]+=x;
}
int main()
{
ll n,op,l,r,x;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&num[i]);
block(n);
for(int i=1;i<=n;i++)
{
scanf("%lld %lld %lld %lld",&op,&l,&r,&x);
if(op==0)
add(l,r,x);
else
printf("%lld\n",num[r]+lazy[pos[r]]);
}
return 0;
}
数列分块入门 2
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。
思路:将每个块进行排序,将没有完全覆盖的块重新更新排序,完整的块利用lower_bound进行答案查找。
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<ctime>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int INF=0x3f3f3f3f;
ll n;
ll num[50005];
ll lazy[505];
ll pos[50005];
ll cnt;
vector<ll>q[505];
void block()
{
memset(lazy,0,sizeof(lazy));
cnt=(ll)sqrt(n);
for(int i=1;i<=n;i++)
{
pos[i]=(i+cnt-1)/cnt;
q[pos[i]].push_back(num[i]);
}
for(int i=1;i<=pos[n];i++)
sort(q[i].begin(),q[i].end());
}
void update(int x)
{
q[x].clear();
for(int i=(x-1)*cnt+1;i<=min(cnt*x,n);i++)
q[x].push_back(num[i]);
sort(q[x].begin(),q[x].end());
}
void add(ll l,ll r,ll x)
{
for(int i=l;i<=min(pos[l]*cnt,r);i++)
num[i]+=x;
update(pos[l]);
if(pos[l]!=pos[r])
{
for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
num[i]+=x;
update(pos[r]);
}
for(int i=pos[l]+1;i<=pos[r]-1;i++)
lazy[i]+=x;
}
int query(ll l,ll r,ll x)
{
int ans=0;
for(int i=l;i<=min(pos[l]*cnt,r);i++)
{
if(num[i]+lazy[pos[l]]<x)
ans++;
}
if(pos[l]!=pos[r])
{
for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
if(num[i]+lazy[pos[r]]<x)
ans++;
}
for(int i=pos[l]+1;i<=pos[r]-1;i++)
{
ll s=x-lazy[i];
int sub=lower_bound(q[i].begin(),q[i].end(),s)-q[i].begin();
ans+=sub;
}
return ans;
}
int main()
{
ll op,l,r,x;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&num[i]);
block();
for(int i=1;i<=n;i++)
{
scanf("%lld %lld %lld %lld",&op,&l,&r,&x);
if(op==0)
add(l,r,x);
else
printf("%d\n",query(l,r,x*x));
}
}
数列分块入门 3
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的前驱(比其小的最大元素)。
思路:和第二道题是一样的,注意题的范围和-1的情况即可
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<ctime>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int INF=0x3f3f3f3f;
ll n;
ll num[100005];
ll lazy[505];
ll pos[100005];
ll cnt;
vector<ll>q[505];
void block()
{
memset(lazy,0,sizeof(lazy));
cnt=(ll)sqrt(n);
for(int i=1;i<=n;i++)
{
pos[i]=(i+cnt-1)/cnt;
q[pos[i]].push_back(num[i]);
}
for(int i=1;i<=pos[n];i++)
sort(q[i].begin(),q[i].end());
}
void update(int x)
{
q[x].clear();
for(int i=(x-1)*cnt+1;i<=min(cnt*x,n);i++)
q[x].push_back(num[i]);
sort(q[x].begin(),q[x].end());
}
void add(ll l,ll r,ll x)
{
for(int i=l;i<=min(pos[l]*cnt,r);i++)
num[i]+=x;
update(pos[l]);
if(pos[l]!=pos[r])
{
for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
num[i]+=x;
update(pos[r]);
}
for(int i=pos[l]+1;i<=pos[r]-1;i++)
lazy[i]+=x;
}
ll query(ll l,ll r,ll x)
{
ll ans=-1;
for(int i=l;i<=min(pos[l]*cnt,r);i++)
if(num[i]+lazy[pos[l]]<x)
ans=max(ans,num[i]+lazy[pos[l]]);
if(pos[l]!=pos[r])
{
for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
if(num[i]+lazy[pos[r]]<x)
ans=max(ans,num[i]+lazy[pos[r]]);
}
for(int i=pos[l]+1;i<=pos[r]-1;i++)
{
ll s=x-lazy[i];
ll sub=lower_bound(q[i].begin(),q[i].end(),s)-q[i].begin();
if(sub)
ans=max(ans,q[i][sub-1]+lazy[i]);
}
return ans;
}
int main()
{
ll op,l,r,x;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&num[i]);
block();
for(int i=1;i<=n;i++)
{
scanf("%lld %lld %lld %lld",&op,&l,&r,&x);
if(op==0)
add(l,r,x);
else
printf("%lld\n",query(l,r,x));
}
}
数列分块入门 4
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,区间求和(取模)
思路:模板题,注意会爆 int
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<ctime>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int INF=0x3f3f3f3f;
int n;
ll num[50005];
ll lazy[50005];
ll sum[50005];
int pos[50005];
int cnt;
void block()
{
memset(lazy,0,sizeof(lazy));
cnt=(int)sqrt(n);
for(int i=1;i<=n;i++)
{
pos[i]=(i+cnt-1)/cnt;
sum[pos[i]]+=num[i];
}
}
void add(int l,int r,ll x)
{
for(int i=l;i<=min(pos[l]*cnt,r);i++)
num[i]+=x;
sum[pos[l]]+=x*(min(pos[l]*cnt,r)-l+1);
if(pos[l]!=pos[r])
{
for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
num[i]+=x;
sum[pos[r]]+=x*(r-(pos[r]-1)*cnt);
}
for(int i=pos[l]+1;i<=pos[r]-1;i++)
lazy[i]+=x;
}
ll query(int l,int r,int mod)
{
ll ans=0;
for(int i=l;i<=min(pos[l]*cnt,r);i++)
ans=(ans+num[i]+lazy[pos[l]])%mod;
if(pos[l]!=pos[r])
{
for(int i=(pos[r]-1)*cnt+1;i<=r;i++)
ans=(ans+num[i]+lazy[pos[r]])%mod;
}
for(int i=pos[l]+1;i<=pos[r]-1;i++)
ans=(ans+lazy[i]*cnt+sum[i])%mod;
return ans;
}
int main()
{
int op,l,r;
ll x;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&num[i]);
block();
for(int i=1;i<=n;i++)
{
scanf("%d %d %d %lld",&op,&l,&r,&x);
if(op==0)
add(l,r,x);
else
printf("%lld\n",query(l,r,x+1));
}
return 0;
}
数列分块入门 5
给出一个长为 n 的数列 ,以及 n 个操作,操作涉及区间开方,区间求和。
思路:因为是开方,所以一个数字最多进行5此操作就会变成 1,如果是0就会是零,我们用一个数组来记录一个块是否还可以开根,类似于剪枝的效果,如果还能开方,暴力就可以了
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<ctime>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int INF=0x3f3f3f3f3f3f;
ll n;
ll num[50005];
ll pos[50005];
ll sum[500];
ll vis[500];
ll cnt;
void block()
{
memset(sum,0,sizeof(sum));
memset(vis,0,sizeof(vis));
cnt=sqrt(n);
for(ll i=1; i<=n; i++)
{
pos[i]=(cnt+i-1)/cnt;
sum[pos[i]]+=num[i];
}
}
void update(ll l,ll r)
{
for(ll i=l; i<=min(pos[l]*cnt,r); i++)
{
sum[pos[l]]-=num[i];
num[i]=sqrt(num[i]);
sum[pos[l]]+=num[i];
}
if(pos[l]!=pos[r])
{
for(ll i=cnt*(pos[r]-1)+1; i<=r; i++)
{
sum[pos[r]]-=num[i];
num[i]=sqrt(num[i]);
sum[pos[r]]+=num[i];
}
}
for(ll i=pos[l]+1; i<=pos[r]-1; i++)
{
if(vis[i]==0)
{
vis[i]=1;
sum[i]=0;
for(ll j=(i-1)*cnt+1; j<=min(i*cnt,n); j++)
{
num[j]=sqrt(num[j]);
sum[i]+=num[j];
if(num[j]>1)
vis[i]=0;
}
}
}
}
ll query(ll l,ll r)
{
ll ans=0;
for(ll i=l; i<=min(pos[l]*cnt,r); i++)
ans+=num[i];
if(pos[l]!=pos[r])
for(ll i=(pos[r]-1)*cnt+1; i<=r; i++)
ans+=num[i];
for(ll i=pos[l]+1; i<=pos[r]-1; i++)
ans+=sum[i];
return ans;
}
int main()
{
ll op,l,r,x;
while(~scanf("%lld",&n))
{
for(ll i=1; i<=n; i++)
scanf("%lld",&num[i]);
block();
for(ll i=1; i<=n; i++)
{
scanf("%lld %lld %lld %lld",&op,&l,&r,&x);
if(op)
printf("%lld\n",query(l,r));
else
update(l,r);
}
}
return 0;
}
数列分块入门 6
给出一个长为 n 的数列,以及 n 个操作,操作涉及单点插入,单点询问,数据随机生成。
思路:我们可以用 vector 来进行块的维护,方便我们进行插入,为了维护复杂度,如果一个块包含的数量太大了,我们应该重新分块。
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <stack>
#include <map>
#include <new>
#include <ctime>
#include <vector>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai = acos(-1.0);
const double E = 2.718281828459;
const int INF = 0x3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
int n;
int num[200005];
int cnt, up; ///cnt 每个块的数量 up块的上限
vector<int> q[505];
void block() {
cnt = sqrt(n);
up = (n + cnt - 1) / cnt;
for (int i = 1; i <= n; i++) {
q[(i + cnt - 1) / cnt].push_back(num[i]);
}
}
int x[200005], sign;
void reblock() {
sign = 0;
for (int i = 1; i <= up; i++) {
for (int j = 0; j < q[i].size(); j++) {
x[++sign] = q[i][j];
}
q[i].clear();
}
cnt = sqrt(sign);
up = (sign + cnt - 1) / cnt;
for (int i = 1; i <= sign; i++) {
q[(i + cnt - 1) / cnt].push_back(x[i]);
}
}
int main() {
int op, l, r, x;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &num[i]);
block();
for (int i = 1; i <= n; i++) {
scanf("%d %d %d %d", &op, &l, &r, &x);
if (op == 0) {
int group, sub;
for (int i = 1; i <= up; i++) {
if (l > q[i].size())
l -= q[i].size();
else {
group = i;
sub = l;
break;
}
}
q[group].insert(q[group].begin() + sub - 1, r);
if (q[group].size() > 7 * cnt)
reblock();
} else {
int group, sub;
for (int i = 1; i <= up; i++) {
if (r > q[i].size())
r -= q[i].size();
else {
group = i;
sub = r;
break;
}
}
printf("%d\n", q[group][sub - 1]);
}
}
return 0;
}
数列分块入门 7
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间乘法,区间加法,单点询问。
思路:
主要考虑如何维护加法和乘法标记。
考虑一般情况,设任意块内中任意元素为x,所携带的加法标记为a,所携带的乘法标记为m。
区间加法:加数为a′,原先的值为mx+a,进行加法后的值为mx+a+a′,所以只需要更新加法标记,变为a+a′。
区间乘法:乘数为m′,原先的值为mx+a,进行乘法后的值为m′mx+am′,所以需要更新加法和乘法标记,乘法标记变为m∗m′,
加法标记变为a∗m′。
对于左右边界块内的部分,首先把所有标记下放,然后暴力更新数据值。
///#include <bits/stdc++.h>
///#include <unordered_map>
///#include <unordered_set>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <stack>
#include <map>
#include <new>
#include <ctime>
#include <vector>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai = acos(-1.0);
const double E = exp(1.0);
const int INF = 0x3f3f3f3f;
const ll mod = 10007;
int n;
ll num[100005];
int pos[100005], cnt;
ll lazy_add[505], lazy_mul[505];
inline void pushdown(int s) {
if (lazy_mul[s] == 1 && lazy_add[s] == 0)
return;
for (int i = (s - 1) * cnt + 1; i <= min(s * cnt, n); ++i) {
num[i] = ((num[i] * lazy_mul[s]) % mod + lazy_add[s]) % mod;
}
lazy_mul[s] = 1;
lazy_add[s] = 0;
}
inline void add(int l, int r, ll x) {
pushdown(pos[l]);
for (int i = l; i <= min(r, cnt * pos[l]); ++i) {
num[i] = (num[i] + x) % mod;
}
if (pos[l] != pos[r]) {
pushdown(pos[r]);
for (int i = (pos[r] - 1) * cnt + 1; i <= r; ++i) {
num[i] = (num[i] + x) % mod;
}
}
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i) {
lazy_add[i] = (lazy_add[i] + x) % mod;
}
}
inline void mul(int l, int r, ll x) {
pushdown(pos[l]);
for (int i = l; i <= min(r, pos[l] * cnt); ++i) {
num[i] = (num[i] * x) % mod;
}
if (pos[l] != pos[r]) {
pushdown(pos[r]);
for (int i = (pos[r] - 1) * cnt + 1; i <= r; ++i) {
num[i] = (num[i] * x) % mod;
}
}
for (int i = pos[l] + 1; i <= pos[r] - 1; ++i) {
lazy_mul[i] = lazy_mul[i] * x % mod;
lazy_add[i] = lazy_add[i] * x % mod;
}
}
inline void init() {
cnt = sqrt(n);
for (int i = 1; i <= n; ++i) {
pos[i] = (i - 1) / cnt + 1;
lazy_mul[pos[i]] = 1;
lazy_add[pos[i]] = 0;
}
}
int main() {
scanf("%d", &n);
init();
for (int i = 1; i <= n; ++i) {
scanf("%lld", &num[i]);
}
int op, l, r;
ll x;
for (int i = 1; i <= n; ++i) {
scanf("%d %d %d %lld", &op, &l, &r, &x);
if (op == 0) {
add(l, r, x);
} else if (op == 1) {
mul(l, r, x);
} else {
printf("%lld\n", ((num[r] * lazy_mul[pos[r]]) % mod + lazy_add[pos[r]]) % mod);
}
}
return 0;
}
数列分块入门 8
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间询问等于一个数 c 的元素,并将这个区间的所有元素改为 c 。
最开始的思路还以为和数列分块入门3类似,一看时间才500ms,绝对不能像3一样来处理。我们可以 lazy 标记一个块是否全部被赋值为同一个数,如果被赋值的数和查询的数相等,答案直接加上块的长度,如果没有被赋值过,即 lazy == -1。我们就暴力判断,然后给该块的 lazy 赋值,不完整的块我们暴力修改即可。
///#include <bits/stdc++.h>
///#include <unordered_map>
///#include <unordered_set>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <stack>
#include <map>
#include <new>
#include <ctime>
#include <vector>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai = acos(-1.0);
const double E = exp(1.0);
const int INF = 0x3f3f3f3f;
int n;
int cnt, num[100005], pos[100005];
int lazy[505];
inline void update(int s, int l, int r, int x) {
for (int i = (s - 1) * cnt + 1; i <= min(s * cnt, n); i++) {
if (i >= l && i <= r) {
num[i] = x;
} else {
if (lazy[s] != -1)
num[i] = lazy[s];
}
}
lazy[s] = -1;
}
inline int query(int l, int r, int x) {
int ans = 0;
if (lazy[pos[l]] == x)
ans = ans + min(r, pos[l] * cnt) - l + 1;
if (lazy[pos[l]] == -1) {
for (int i = l; i <= min(r, pos[l] * cnt); i++) {
if (num[i] == x)
ans++;
}
}
update(pos[l], l, min(pos[l] * cnt, r), x);///块 变为x的区间 x
if (pos[l] != pos[r]) {
if (lazy[pos[r]] == x) {
ans = ans + r - (pos[r] - 1) * cnt;
}
if (lazy[pos[r]] == -1) {
for (int i = (pos[r] - 1) * cnt + 1; i <= r; i++) {
if (num[i] == x)
ans++;
}
}
update(pos[r], (pos[r] - 1) * cnt + 1, r, x);
}
for (int i = pos[l] + 1; i < pos[r]; i++) {
if (lazy[i] == x)
ans = ans + min(i * cnt, n) - (i - 1) * cnt;
if (lazy[i] == -1) {
for (int j = (i - 1) * cnt + 1; j <= min(i * cnt, n); j++) {
if (num[j] == x)
ans++;
}
}
lazy[i] = x;
}
return ans;
}
int main() {
memset(lazy, -1, sizeof(lazy));///lazy 标记初始化
int l, r, x;
scanf("%d", &n);
cnt = sqrt(n); ///块的大小
for (int i = 1; i <= n; i++) {
pos[i] = (i + cnt - 1) / cnt;
scanf("%d", &num[i]);
}
for (int i = 1; i <= n; i++) {
scanf("%d %d %d", &l, &r, &x);
printf("%d\n", query(l, r, x));
}
return 0;
}
数列分块入门 9
给出一个长为 n 的数列,以及 n 个操作,操作涉及询问区间的最小众数。
参考博客:https://blog.csdn.net/yiqzq/article/details/80672013
第一种写法代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int mod = 10007;
int n, block, belog[maxn], l, r, m;
int a[maxn], t[maxn], really[maxn]; // a是原数组,t是拷贝数组,really[i]=j,表示经过离散化的数字i的原数是j
int dp[2005][2005]; // dp[i][j]表示第i块到第j块的众数是多少(注意此题是经过离散化的众数)
vector<int> v[maxn]; //存的数字i的分别位于哪些位置
int num[maxn];
inline int read() { //读入挂
int ret = 0, c, f = 1;
for (c = getchar(); !(isdigit(c) || c == '-'); c = getchar())
;
if (c == '-')
f = -1, c = getchar();
for (; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
if (f < 0)
ret = -ret;
return ret;
}
void init(int x) { //预处理第i块到第j块的众数是几
int Max_num = 0; //当前众数的数量
memset(num, 0, sizeof(num));
int mode = 0; //众数
for (int i = (x - 1) * block + 1; i <= n; i++) { //暴力预处理
int t = belog[i];
num[a[i]]++;
if (num[a[i]] > Max_num) {
Max_num = num[a[i]];
mode = a[i];
}
if (Max_num == num[a[i]] && mode > a[i])
mode = a[i]; //因为要求的是最小众数
dp[x][t] = mode; //表示第x块到第t块的众数
}
}
int solve(int l, int r, int a) { //查找a的出现次数
vector<int>::iterator x = upper_bound(v[a].begin(), v[a].end(), r);
/*
用upper_bound()好处在于不用+1了,因为upper_bound()已经帮你加过1了
这里用两个lower_bound(),然后下面改成x-y+1 是错的,应该是一些特殊范围导致的
*/
vector<int>::iterator y = lower_bound(v[a].begin(), v[a].end(), l);
return x - y;
}
int query(int l, int r) {
int t1 = belog[l];
int t2 = belog[r];
int mode = dp[t1 + 1][t2 - 1]; //判断中间完整块的众数
int Max_num = solve(l, r, mode);
for (int i = l; i <= min(r, t1 * block); i++) { //暴力左边不完整区间
int t = solve(l, r, a[i]); // t是数量
if (Max_num < t || (Max_num == t && mode > a[i])) {
Max_num = t;
mode = a[i];
}
}
if (t1 != t2) {
for (int i = (t2 - 1) * block + 1; i <= r; i++) {
int t = solve(l, r, a[i]);
if (Max_num < t || (Max_num == t && mode > a[i])) {
Max_num = t;
mode = a[i];
}
}
}
return mode;
}
int main() {
n = read();
block = 50; //开50比sqrt(n)速度要块好多
for (int i = 1; i <= n; i++) {
a[i] = read();
t[i] = a[i];
belog[i] = (i - 1) / block + 1;
}
sort(t + 1, t + 1 + n);
m = unique(t + 1, t + 1 + n) - t - 1; //离散化
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(t + 1, t + 1 + m, a[i]) - t;
}
for (int i = 1; i <= n; i++) {
v[a[i]].push_back(i);
}
for (int i = 1; i <= belog[n]; i++) {
init(i);
}
for (int i = 1; i <= n; i++) {
l = read();
r = read();
printf("%d\n", t[query(l, r)]);
}
return 0;
}