爆肝一天,终于写完了
由于后半部分参考了dalao的博客,于是码风较前边有点区别
第一题
区间修改单点查询
讲真这题可以用CDQ分治写,而且通俗易懂,但这篇讲的是分块,所以我们只是贴一下代码:
CDQ分治版:
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 1000010;
struct nod
{
ll x , y;
int id;
} p[N] , tmp[N];
ll ans[N] , num[N] , tot;
int n , m ;
ll a[N];
int c;
void solve(int l , int r)
{
if(l >= r) return;
int mid = (l + r) >> 1 , i = l , j = mid + 1 , k = 0 , sum = 0;
solve(l , mid) ; solve(mid + 1 , r);
while(i <= mid && j <= r)
{
if(p[i].x <= p[j].x)
{
if(!p[i].id)
sum += p[i].y;
tmp[k++] = p[i++];
}
else
{
if(p[j].id)
ans[p[j].id] += sum;
tmp[k++] = p[j++];
}
}
while(i <= mid) tmp[k++] = p[i++];
while(j <= r)
{
if(p[j].id)
ans[p[j].id] += sum;
tmp[k++] = p[j++];
}
for(int i = 0 ; i < k ; i++) p[l + i] = tmp[i];
}
int main()
{
scanf("%d%d" , &n , &m);
for(int i = 1 ; i <= n ; i++) cin >> a[i];
int cnt = 0;
for(int i = 1 , l , r , d ; i <= m ; i++)
{
cin >> c;
if(c == 1)
{
cin >> l >> r >> d;
p[++cnt].x = l ; p[cnt].y = d ; p[cnt].id = 0;
p[++cnt].x = r + 1 ; p[cnt].y = -d ; p[cnt].id = 0;
}
else
{
cin >> l;
p[++cnt].x = l ; p[cnt].id = ++tot ; num[tot] = l;
}
}
solve(1 , cnt);
for(int i = 1 ; i <= tot ; i++) printf("%d\n" , ans[i] + a[num[i]]);
return 0;
}
回到正题,这题用对于每一块维护一个延迟标记(add),如果是在修改时覆盖了整一个块,那我们就只对标记进行修改,块内的值我们暂时不处理
对于区间两侧不完整的块我们暴力修改内部,不更新相应的延迟标记
code:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int N = 50010;
struct block
{
int l , r , add;
} q[N];
int n , LEN;
int a[N] , belong[N];
int main()
{
scanf("%d" , &n);
LEN = sqrt(n);
int cnt = 0;
for(int L = 1 , R ; L <= n ; L++)
{
R = L + LEN - 1;
q[++cnt].l = L , q[cnt].r = R;
q[cnt].add = 0;
L = R;
}
for(int i = 1 ; i <= n ; i++)
{
scanf("%d" , &a[i]);
belong[i] = (i - 1) / LEN + 1;
}
for(int i = 1 ; i <= n ; i++)
{
int opt , L , R , c;
scanf("%d%d%d%d" , &opt , &L , &R , &c);
R = min(R , n);
if(opt == 0)
{
if(belong[L] == belong[R])
{
for(int i = L ; i <= R ; i++) a[i] += c; continue;
}
int k = belong[L];
for(int i = L ; i <= q[k].r ; i++) a[i] += c;
k = belong[R];
for(int i = q[k].l ; i <= R ; i++) a[i] += c;
for(int i = belong[L] + 1 ; i <= belong[R] - 1 ; i++) q[i].add += c;
}
else
printf("%d\n" , a[R] + q[belong[R]].add);
}
return 0;
}
第二题
区间修改+区间内小于某个数的个数
由小于二字想到块内二分,观察数据范围发现可行
对于每个块维护块内有序数组a以及原来的数组num(当时写的时候脑抽还多写了一个data来存一开始的数据,其实是不需要的),由于是区间加法,我们仿照上一题添加一个延迟标记
对于查询,两侧不完整的块暴力枚举判断即可,而对于完整的块,我们对块内元素二分(注意要减去这个块上的延迟标记),最后得到的下标即为我们找到的个数
对于修改,两侧不完整的块枚举处理完后还要维护相应块内的有序数组,而中间完整的则直接更新延迟标记即可
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 100010 , M = 1010;
struct block
{
int l , r , add;
ll a[M] , len;
ll num[M];
int find(int x)
{
int L = 1 , R = len , mid , best = -1;
while(L <= R)
{
mid = (L + R) >> 1;
if(a[mid] < x)
{
best = mid;
L = mid + 1;
}else R = mid - 1;
}return best;
}
void iint()
{
for(int i = 1 ; i <= len ; i++) a[i] = num[i];
sort(a + 1 , a + len + 1);
}
} q[N];
int LEN;
ll data[N] , bel[N] , n;
int main()
{
scanf("%d" , &n);
LEN = sqrt(n);
int cnt = 0;
for(int L = 1 , R ; L <= n ; L++)
{
R = L + LEN - 1;
q[++cnt].l = L , q[cnt].r = R;
q[cnt].add = q[cnt].len = 0;
L = R;
}
for(int i = 1 ; i <= n ; i++)
{
scanf("%lld" , &data[i]);
bel[i] = (i - 1) / LEN + 1;
q[bel[i]].num[++q[bel[i]].len] = data[i];
}
for(int i = 1 ; i <= cnt ; i++) q[i].iint();
for(int i = 1 ; i <= n ; i++)
{
ll opt , L , R , c;
scanf("%lld%lld%lld%lld" , &opt , &L , &R , &c);
if(opt == 0)
{
if(bel[L] == bel[R])
{
int k1 = (L - 1) % LEN + 1 , k2 = (R - 1) % LEN + 1;
for(int i = k1 ; i <= k2 ; i++)
q[bel[L]].num[i] += c;
q[bel[L]].iint();
continue;
}
int k1 = (L - 1) % LEN + 1 , k2 = (R - 1) % LEN + 1;
for(int i = k1 ; i <= LEN ; i++)
q[bel[L]].num[i] += c;
for(int i = 1 ; i <= k2 ; i++)
q[bel[R]].num[i] += c;
for(int i = bel[L] + 1 ; i <= bel[R] - 1 ; i++) q[i].add += c;
q[bel[L]].iint() ; q[bel[R]].iint();
}
else
{
c = c * c;
int k1 = (L - 1) % LEN + 1 , k2 = (R - 1) % LEN + 1 ;
ll res = 0;
if(bel[L] == bel[R])
{
for(int i = k1 ; i <= k2 ; i++) if(q[bel[L]].num[i] + q[bel[L]].add < c) res++;
printf("%lld\n" , res);
continue;
}
for(int i = k1 ; i <= LEN ; i++) if(q[bel[L]].num[i] + q[bel[L]].add < c) res++;
for(int i = 1 ; i <= k2 ; i++) if(q[bel[R]].num[i] + q[bel[R]].add < c) res++;
for(int i = bel[L] + 1 ; i <= bel[R] - 1 ; i++)
{
int x = q[i].find(c - q[i].add);
if(x > 0) res += x;
}
printf("%lld\n" , res);
}
}
return 0;
}
第三题
区间修改+查前驱
差前驱等价于求最大的小于某个数的元素
于是仿照第二题的做法即可
对于查询,不完整的块枚举更新,完整的则二分(还是要注意减去延迟标记)
修改几乎更第二题一样
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 100010 , M = 350;
struct block
{
int l , r , add;
ll a[M];
ll num[M] , len;
ll find(int x)
{
int l = 1 , r = len , mid , best = 0;
while(l <= r)
{
mid = (l + r) >> 1;
if(a[mid] < x)
{
best = mid;
l = mid + 1;
}else r = mid - 1;
}return a[best];
}
void iint()
{
for(int i = 1 ; i <= len ; i++) a[i] = num[i];
sort(a + 1 , a + len + 1);
}
} q[N];
int n , LEN , bel[N];
void updata(int L , int R , ll c)
{
int k1 = (L - 1) % LEN + 1 , k2 = (R - 1) % LEN + 1;
if(bel[L] == bel[R])
{
for(int i = k1 ; i <= k2 ; i++) q[bel[L]].num[i] += c;
q[bel[L]].iint() ; return;
}
for(int i = k1 ; i <= LEN ; i++) q[bel[L]].num[i] += c;
for(int i = 1 ; i <= k2 ; i++) q[bel[R]].num[i] += c;
for(int i = bel[L] + 1 ; i < bel[R] ; i++) q[i].add += c;
q[bel[L]].iint() ; q[bel[R]].iint();
}
void query(int L , int R , ll c)
{
int k1 = (L - 1) % LEN + 1 , k2 = (R - 1) % LEN + 1 ;
ll ans = -1;
if(bel[L] == bel[R])
{
for(int i = k1 ; i <= k2 ; i++)
if(q[bel[L]].num[i] + q[bel[L]].add < c)
ans = max(ans , q[bel[L]].num[i] + q[bel[L]].add);
printf("%lld\n" , ans); return;
}
for(int i = k1 ; i <= LEN ; i++)
if(q[bel[L]].num[i] + q[bel[L]].add < c) ans = max(ans , q[bel[L]].num[i] + q[bel[L]].add);
for(int i = 1 ; i <= k2 ; i++)
if(q[bel[R]].num[i] + q[bel[R]].add < c) ans = max(ans , q[bel[R]].num[i] + q[bel[R]].add);
for(int i = bel[L] + 1 ; i < bel[R] ; i++)
{
ll res = q[i].find(c - q[i].add);
if(res < c - q[i].add) ans = max(ans , res + q[i].add);
}
printf("%lld\n" , ans); return;
}
int main()
{
scanf("%d" , &n);
LEN = sqrt(n);
int cnt = 0;
for(int L = 1 , R ; L <= n ; L++)
{
R = min(L + LEN - 1 , n);
q[++cnt].l = L , q[cnt].r = R;
q[cnt].add = q[cnt].len = 0;
q[cnt].a[0] = -1;
L = R;
}
for(int i = 1 ; i <= n ; i++)
{
ll x;
scanf("%lld" , &x);
bel[i] = (i - 1) / LEN + 1;
q[bel[i]].num[++q[bel[i]].len] = x;
}
for(int i = 1 ; i <= cnt ; i++) q[i].iint();
for(int i = 1 ; i <= n ; i++)
{
ll opt , L , R , c;
scanf("%lld%lld%lld%lld" , &opt , &L , &R , &c);
if(opt == 0)
updata(L , R , c);
else
query(L , R , c);
}
return 0;
}
第四题
仿照第一题延迟标记,维护多一个块内的元素和
对于查询,不完整的标记下放,完整的直接塞元素和跟标记(记得乘块长)
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 50010 , M = 310;
struct block
{
int l , r;
ll sum , add;
} q[N];
int n , LEN , bel[N];
ll a[N];
void updata(int L , int R , ll c)
{
if(bel[L] == bel[R])
{
for(int i = L ; i <= R ; i++) a[i] += c;
q[bel[L]].sum += c * (R - L + 1);
return;
}
for(int i = L ; i <= q[bel[L]].r ; i++) a[i] += c;
q[bel[L]].sum += c * (q[bel[L]].r - L + 1);
for(int i = q[bel[R]].l ; i <= R ; i++) a[i] += c;
q[bel[R]].sum += c * (R - q[bel[R]].l + 1);
for(int i = bel[L] + 1 ; i < bel[R] ; i++) q[i].add += c;
}
void query(int L , int R , ll c)
{
ll ans = 0;
if(bel[L] == bel[R])
{
for(int i = L ; i <= R ; i++) ans = (ans + a[i] + q[bel[L]].add) % c;
printf("%lld\n" , ans) ; return;
}
for(int i = L ; i <= q[bel[L]].r ; i++) ans = (ans + a[i] + q[bel[L]].add) % c;
for(int i = q[bel[R]].l ; i <= R ; i++) ans = (ans + a[i] + q[bel[R]].add) % c;
for(int i = bel[L] + 1 ; i < bel[R] ; i++)
ans = (ans + q[i].sum + q[i].add * (q[i].r - q[i].l + 1)) % c;
printf("%lld\n" , ans) ; return;
}
int main()
{
scanf("%d" , &n);
LEN = sqrt(n);
int cnt = 0;
for(int L = 1 , R ; L <= n ; L++)
{
R = L + LEN - 1; q[++cnt].l = L , q[cnt].r = min(R , n);
q[cnt].add = q[cnt].sum = 0;
L = R;
}
for(int i = 1 ; i <= n ; i++)
{
scanf("%lld" , &a[i]);
bel[i] = (i - 1) / LEN + 1;
q[bel[i]].sum += a[i];
}
for(int i = 1 ; i <= n ; i++)
{
int opt , L , R;
ll c;
scanf("%d%d%d%lld" , &opt , &L , &R , &c);
if(opt == 0)
updata(L , R , c);
else
query(L , R , c + 1);
}
return 0;
}
第五题
区间元素开方+区间求和
仿照第四题
有个性质:
当x = 0或1时,对x开方还是x
于是再加多一个标记,若块内元素都是0和1时,修改操作直接跳过即可
修改则暴力枚举开方,再维护块内元素和
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 60010;
int a[N];
int bel[N] , n , LEN ;
struct block
{
int l , r;
int op ;
int sum;
bool check()
{
for(int i = l ; i <= r ; i++)
if(a[i] > 1) return false;
return true;
}
} q[N];
void updata(int L , int R)
{
if(bel[L] == bel[R])
{
if(q[bel[L]].op == 1) return;
for(int i = L ; i <= R ; i++)
{
if(a[i] < 0) continue;
q[bel[L]].sum -= a[i];
a[i] = floor(sqrt(a[i]));
q[bel[L]].sum += a[i];
}
if(q[bel[L]].check()) q[bel[L]].op = 1;
return;
}
if(!q[bel[L]].op)
{
for(int i = L ; i <= q[bel[L]].r ; i++)
{
if(a[i] < 0) continue;
q[bel[L]].sum -= a[i]; a[i] = floor(sqrt(a[i]));
q[bel[L]].sum += a[i];
}
if(q[bel[L]].check()) q[bel[L]].op = 1;
}
if(!q[bel[R]].op)
{
for(int i = q[bel[R]].l ; i <= R ; i++)
{
if(a[i] < 0) continue;
q[bel[R]].sum -= a[i] ; a[i] = floor(sqrt(a[i]));
q[bel[R]].sum += a[i];
}
if(q[bel[R]].check()) q[bel[R]].op = 1;
}
for(int i = bel[L] + 1 ; i < bel[R] ; i++)
{
if(q[i].op) continue;
for(int j = q[i].l ; j <= q[i].r ; j++)
{
if(a[j] < 0) continue;
q[i].sum -= a[j] ; a[j] = floor(sqrt(a[j]));
q[i].sum += a[j];
}if(q[i].check()) q[i].op = 1;
}
}
void query(int L , int R)
{
int ans = 0;
if(bel[L] == bel[R])
{
for(int i = L ; i <= R ; i++) ans += a[i];
printf("%d\n" , ans); return;
}
for(int i = L ; i <= q[bel[L]].r ; i++) ans += a[i];
for(int i = q[bel[R]].l ; i <= R ; i++) ans += a[i];
for(int i = bel[L] + 1 ; i < bel[R] ; i++)
ans += q[i].sum;
printf("%d\n" , ans);return;
}
int main()
{
// freopen("a1.in" , "r" , stdin);
// freopen("ans.out" , "w" , stdout);
scanf("%d" , &n);
LEN = sqrt(n);
int cnt = 0;
for(int L = 1 , R ; L <= n ; L++)
{
R = min(L + LEN - 1 , n);
q[++cnt].l = L , q[cnt].r = R ;
q[cnt].sum = q[cnt].op = 0;
L = R;
}
for(int i = 1 ; i <= n ; i++)
{
scanf("%d" , &a[i]);
bel[i] = (i - 1) / LEN + 1;
q[bel[i]].sum += a[i];
}
for(int i = 1 ; i <= n ; i++)
{
int opt , L , R , c;
scanf("%d%d%d%d" , &opt , &L , &R , &c);
if(opt == 0)
updata(L , R);
else
query(L , R);
}
return 0;
}
第六题
动态数组vector+分块重构
当某个块太长或者怎样怎样(随便啦~)时就重构
讲白了就是把数据拷贝一份,更新下块长,最后再一个个塞回去就行了
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 100010 , M = 500010;
int a[M] ;
vector<int> v[N];
int LEN , n , m;
void build(int n)
{
LEN = sqrt(n);
m = (n - 1) / LEN + 1;
for(int i = 1 ; i <= n ; i++) scanf("%d" , &a[i]);
for(int i = 1 ; i <= n ; i++) v[(i - 1) / LEN + 1].push_back(a[i]);
}
pair<int , int> query(int b)
{
int x = 1;
while(b > v[x].size())
{
b -= v[x].size() ; x++;
}
return make_pair(x , b - 1);
}
void rebuild()
{
int top = 0;
for(int i = 1 ; i <= m ; i++)
{
for(vector<int> :: iterator it = v[i].begin() ; it != v[i].end() ; it++)
a[++top] = *it;
v[i].clear();
}
LEN = sqrt(top);
for(int i = 1 ; i <= top ; i++)
v[(i - 1) / LEN + 1].push_back(a[i]);
m = (n - 1) / LEN + 1;
}
void Insert(int a , int b)
{
pair<int , int> t = query(a);
v[t.first].insert(v[t.first].begin() + t.second , b);
if(v[t.first].size() > 20 * LEN) rebuild();
}
int main()
{
scanf("%d" , &n);
build(n);
for(int i = 1 ; i <= n ; i++)
{
int opt , L , R , c;
scanf("%d%d%d%d" , &opt , &L , &R , &c);
if(opt == 0)
Insert(L , R);
else
{
pair<int , int> t = query(R);
printf("%d\n" , v[t.first][t.second]);
}
}
return 0;
}
第七题
仿照第四题做法,只不过乘法跟加法分开成两个标记
由于a * c = ac,(a + m) * c = ac + mc
因此我们在进行乘法操作时是需要修改加法的标记的
对于不完整的块则需要将标记下放,再赋初值上去
code:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100010 , MOD = 10007;
struct block
{
int l , r;
int sum , mx;
} q[N];
int n , LEN;
int bel[N] , a[N];
void reset(int L)
{
for(int i = q[bel[L]].l ; i <= q[bel[L]].r ; i++) a[i] = (a[i] * q[bel[L]].mx + q[bel[L]].sum) % MOD;
q[bel[L]].sum = 0 , q[bel[L]].mx = 1;
}
void updata(int L , int R , int c , int opt)
{
reset(L);
for(int i = L ; i <= min(q[bel[L]].r , R) ; i++)
{
if(opt == 0)
a[i] = (a[i] + c) % MOD;
else a[i] = (a[i] * c) % MOD;
}
if(bel[L] != bel[R])
{
reset(R);
for(int i = q[bel[R]].l ; i <= R ; i++)
if(opt == 0) a[i] = (a[i] + c) % MOD;
else a[i] = (a[i] * c) % MOD;
}
for(int i = bel[L] + 1 ; i < bel[R] ; i++)
if(opt == 0)
q[i].sum = (q[i].sum + c) % MOD;
else
{
q[i].sum = (q[i].sum * c) % MOD;
q[i].mx = (q[i].mx * c) % MOD;
}
}
int main()
{
scanf("%d" , &n);
LEN = sqrt(n);
int cnt = 0;
for(int L = 1 , R ; L <= n ; L++)
{
R = L + LEN - 1;
q[++cnt].l = L , q[cnt].r = min(R , n);
q[cnt].sum = 0;
q[cnt].mx = 1;
L = R;
}
for(int i = 1 ; i <= n ; i++)
{
scanf("%d" , &a[i]);
bel[i] = (i - 1) / LEN + 1;
}
for(int i = 1 ; i <= n ; i++)
{
int opt , L , R , c;
scanf("%d%d%d%d" , &opt , &L , &R , &c);
if(opt == 0 || opt == 1) updata(L , R , c , opt);
else printf("%d\n" , (a[R] * q[bel[R]].mx + q[bel[R]].sum) % MOD);
}
return 0;
}
第八题
仿照第五题,块内都是一个值时打标记,查询的时候直接用就行了
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 100010;
int a[N] , n , LEN;
int bel[N];
struct block
{
int l , r;
int id;
bool check()
{
for(int i = l + 1 ; i <= r ; i++)
if(a[i] != a[l]) return false;
return true;
}
} q[N];
void reset(int L)
{
if(q[bel[L]].id == -1) return;
else
{
for(int i = q[bel[L]].l ; i <= q[bel[L]].r ; i++)
a[i] = q[bel[L]].id;
}
q[bel[L]].id = -1;
}
void query(int L , int R , int c)
{
int res = 0;
reset(L);
for(int i = L ; i <= min(q[bel[L]].r , R) ; i++)
{
if(a[i] == c) res++;
a[i] = c;
}
if(bel[L] != bel[R])
{
reset(R);
for(int i = q[bel[R]].l ; i <= R ; i++)
{
if(a[i] == c) res++;
a[i] = c;
}
}
for(int i = bel[L] + 1 ; i < bel[R] ; i++)
{
if(q[i].id == -1)
{
for(int j = q[i].l ; j <= q[i].r ; j++)
{
if(a[j] == c) res++;
a[j] = c;
}
}
else if(q[i].id == c) res += LEN;
q[i].id = c;
}
printf("%d\n" , res);
}
int main()
{
scanf("%d" , &n);
LEN = sqrt(n);
int cnt = 0;
for(int L = 1 , R ; L <= n ; L++)
{
R = L + LEN - 1;
q[++cnt].l = L , q[cnt].r = min(R , n);
q[cnt].id = -1; L = R;
}
for(int i = 1 ; i <= n ; i++)
{
scanf("%d" , &a[i]);
bel[i] = (i - 1) / LEN + 1;
}
for(int i = 1 ; i <= n ; i++)
{
int L , R , c;
scanf("%d%d%d" , &L , &R , &c);
query(L , R , c);
}
return 0;
}
第九题
仿照《进阶指南》里蒲公英的做法,用vector存每个值的下标再二分即可,(除开可以用离线做法做以外,题都一毛一样的)
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
using namespace std;
const int N = 100010;
int n , LEN ;
int bel[N] , a[N] , val[N];
int MAX[2020][2020];
int num[N];
vector<int> v[N];
map<int , int> mp;
bool visit[N];
void iint(int x)
{
int count = 0 , maxn1 = 0;
memset(num , 0 , sizeof num);
for(int i = (x - 1) * LEN + 1 ; i <= n ; i++)
{
num[a[i]]++;
if(num[a[i]] > count || (num[a[i]] == count && val[a[i]] < val[maxn1]))
{
count = num[a[i]];
maxn1 = a[i];
}
MAX[x][bel[i]] = maxn1;
}
}
int er(int x , int l , int r)
{
return upper_bound(v[x].begin() , v[x].end() , r) - lower_bound(v[x].begin() , v[x].end() , l);
}
int solve(int l , int r)
{
int ans = MAX[bel[l] + 1][bel[r] - 1] , count = 0;
memset(visit , 0 , sizeof visit);
count = er(ans , l , r);
for(int i = l ; i <= min(bel[l] * LEN , r) ; i++)
{
if(visit[a[i]] == 0)
{
visit[a[i]] = 1;
int res = er(a[i] , l , r);
if(res > count || (res == count && val[a[i]] < val[ans])) count = res , ans = a[i];
}
}
if(bel[l] != bel[r])
{
for(int i = (bel[r] - 1) * LEN + 1 ; i <= r ; i++)
{
if(visit[a[i]] == 0)
{
visit[a[i]] = 1;
int res = er(a[i] , l , r);
if(res > count || (res == count && val[a[i]] < val[ans])) count = res , ans = a[i];
}
}
}
return val[ans];
}
int main()
{
scanf("%d" , &n);
LEN = 80;
int cnt = 0 , cnt1 = 0;
for(int i = 1 ; i <= n ; i++)
{
scanf("%d" , &a[i]);
if(mp[a[i]] == 0)
{
mp[a[i]] = ++cnt;
val[cnt] = a[i];
}
a[i] = mp[a[i]];
v[a[i]].push_back(i);
bel[i] = (i - 1) / LEN + 1;
}
for(int i = 1 ; i <= bel[n] ; i++) iint(i);
for(int i = 1 ; i <= n ; i++)
{
int L , R;
scanf("%d%d" , &L , &R);
printf("%d\n" , solve(L , R));
}
return 0;
}