dl题解 _「分块」数列分块入门1 – 9 by hzwer
LOJ #6277. 数列分块入门 1
- 题意:给出一个长为n的数列,以及n个操作,操作涉及区间加法,单点查值。
- 时间限制:100ms
分块
- 我们将整个数列划分为很多块,暂且分为n / m块,用block[ i ]记录第 i 个数据arr[ i ]对应的块的个数。O(n)【数据读入时就可以操作完成,block[ i ] = (i - 1) / m + 1】
- 如果操作的区间涉及我们划分的“一整块”,那么我们可以直接用一个标记数组lazy[ ]来记录该“块”的add值。最大O(n / m)
- 而操作区间两侧的“非完整的块”,至多有2m个元素,那么就直接暴力更新每个数据的值。最大O(m)
- 所以区间操作的复杂度就是O(m) + O(n / m),根据均值不等式:a + b >= 2sqrt(ab),可以得到当m = sqrt(n)时,复杂度最优
- 因为只有单点查询,所以某个点的答案就是arr[ i ] + lazy[ block[ i ] ]。O(1)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)
#define MID (l + r ) >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define eps 1e-6
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 5e4 + 5;
int n, m, arr[maxN];
int block[maxN];
int lazy[maxN];
void add(int l, int r, int val)
{
if(block[l] == block[r])//在一个块内,暴力
{
for(int i = l; i <= r; i ++ )
arr[i] += val;
}
else
{
for(int i = l; i <= block[l] * m; i ++ )//非整块的左边,暴力
arr[i] += val;
for(int i = (block[r] - 1) * m + 1; i <= r; i ++ )//非整块的右边,暴力
arr[i] += val;
for(int i = block[l] + 1; i <= block[r] - 1; i ++ )//中间整块,标记数组记上
lazy[i] += val;
}
}
int main()
{
scanf("%d", &n); m = sqrt(n);
for(int i = 1; i <= n; i ++ )
{
scanf("%d", &arr[i]);
block[i] = (i - 1) / m + 1;
}
for(int i = 1; i <= n; i ++ )
{
int op, l, r, c; scanf("%d%d%d%d", &op, &l, &r, &c);
if(op == 0)//加
add(l, r, c);
else
printf("%d\n", arr[r] + lazy[block[r]]);
}
return 0;
}
LOJ #6278. 数列分块入门 2
- 题意:给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。
- 思路:500ms
分块
- 同样的,我们将整个区间划分为
个区间,每个区间有
个元素。
- 对于查询操作:因为我们要找区间内小于某个值x的元素,那我们就想了,如果每个块都是有序的,那么我们直接lower_bound就
找到“整块”中满足条件的个数。于是我们想到要给划分的块块内排序。所有块排序复杂度:
- 那么对于“非完整块”,我们还是直接暴力查找。
- 对于修改操作:我们需要考虑,进行加的操作的时候,可以会改变“块”内的有序性。所以我们我们需要对这些“非完整块”进行重新排序。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)
#define MID (l + r ) >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define eps 1e-6
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 5e4 + 5;
inline int read()
{
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
int n, m;
int arr[maxN], block[maxN], lazy[maxN];
vector<int>vt[maxN];
void reset(int x)
{
vt[x].clear();
for(int i = (x - 1) * m + 1; i <= x * m; i ++ )
vt[x].push_back(arr[i]);
sort(vt[x].begin(), vt[x].end());
}
void add(int l, int r, int val)
{
if(block[l] == block[r])
{
for(int i = l; i <= r; i ++ )
arr[i] += val;
reset(block[l]);
}
else
{
for(int i = l; i <= block[l] * m; i ++ )
arr[i] += val;
reset(block[l]);
for(int i = (block[r] - 1) * m + 1; i <= r; i ++ )
arr[i] += val;
reset(block[r]);
for(int i = block[l] + 1; i <= block[r] - 1; i ++ )
lazy[i] += val;
}
}
int query(int l, int r, int val)
{
int ans = 0;
if(block[l] == block[r])
{
for(int i = l; i <= r; i ++ )
if(arr[i] + lazy[block[i]] < val)
ans ++;
}
else
{
for(int i = l; i <= block[l] * m; i ++ )
if(arr[i] + lazy[block[i]] < val)
ans ++;
for(int i = (block[r] - 1) * m + 1; i <= r; i ++ )
if(arr[i] + lazy[block[i]] < val)
ans ++;
for(int i = block[l] + 1; i <= block[r] - 1; i ++ )
ans += lower_bound(vt[i].begin(), vt[i].end(), val - lazy[i]) - vt[i].begin();
}
return ans;
}
int main()
{
n = read(); m =sqrt(n);
for(int i = 1; i <= n; i ++ )
{
arr[i] = read();
block[i] = (i - 1) / m + 1;
vt[block[i]].push_back(arr[i]);
}
for(int i = 1; i <= block[n]; i ++ )
sort(vt[i].begin(), vt[i].end());
for(int i = 1; i <= n; i ++ )
{
int op, l, r, c; op = read(); l = read(); r = read(); c = read();
if(op == 0) add(l, r, c);
else printf("%d\n", query(l, r, c * c));
}
return 0;
}
LOJ #6279. 数列分块入门 3
- 题意:给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的前驱(比其小的最大元素)。
- 跟上个题差不多,稍微改一下二分即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll ;
ll read()
{
ll x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 100005;
ll n, m, a[maxN], block[maxN], lazy[maxN];
vector<ll>vt[maxN];
void reset(ll x) {
vt[x].clear();
for(ll i = (x - 1) * m + 1; i <= x * m; ++ i ) {
vt[x].emplace_back(a[i]);
}
sort(vt[x].begin(), vt[x].end());
}
void add(ll l, ll r, ll val) {
if(block[l] == block[r]) {
for(ll i = l; i <= r; ++ i ) {
a[i] += val;
}
reset(block[l]);
} else {
for(ll i = l; i <= block[l] * m; ++ i ) {
a[i] += val;
}
reset(block[l]);
for(ll i = (block[r] - 1) * m + 1; i <= r; ++ i ) {
a[i] += val;
}
reset(block[r]);
for(ll i = block[l] + 1; i <= block[r] - 1; ++ i ) {
lazy[i] += val;
}
}
}
ll query(ll l, ll r, ll val) {
ll ans = INT64_MIN;
if(block[l] == block[r]) {
for(ll i = l; i <= r; ++ i ) {
if(a[i] + lazy[block[l]] < val) {
ans = max(ans, a[i] + lazy[block[l]]);
}
}
} else {
for(ll i = l; i <= block[l] * m; ++ i ) {
if(a[i] + lazy[block[l]] < val) {
ans = max(ans, a[i] + lazy[block[l]]);
}
}
for(ll i = (block[r] - 1) * m + 1; i <= r; ++ i ) {
if(a[i] + lazy[block[r]] < val) {
ans = max(ans, a[i] + lazy[block[r]]);
}
}
for(ll i = block[l] + 1; i <= block[r] - 1; ++ i ) {
ll id = lower_bound(vt[i].begin(), vt[i].end(), val - lazy[i]) - vt[i].begin();
if(id) ans = max(ans, vt[i][id - 1] + lazy[i]);
}
}
return ans == INT64_MIN ? -1 : ans;
}
int main()
{
n = read(); m = sqrt(n);
for(ll i = 1; i <= n; ++ i ) {
a[i] = read();
block[i] = (i - 1) / m + 1;
vt[block[i]].emplace_back(a[i]);
}
for(ll i = 1; i <= block[n]; ++ i ) {
sort(vt[i].begin(), vt[i].end());
}
for(ll i = 1; i <= n; ++ i ) {
ll op, l, r, c;
op = read(), l = read(), r = read(), c = read();
if(!op) { //加
add(l, r, c);
} else { //query
cout << query(l, r, c) << endl;
}
}
return 0;
}
LOJ #6280. 数列分块入门 4
- 题意:给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,区间求和。
- 思路:定义sum[ ]数组表示对应“块”的元素和。分块后进行加操作时,区间中的整块就用lazy[ ]标记,非整块就直接暴力遍历每个元素加,同时元素所在块的sum[ ]也要加上对应的值。区间求和查询时,对于非整块直接暴力查询加上a[i]+lazy[block[ ]],对于整块则加上sum[i] + lazy[i] * m。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll ;
ll read()
{
ll x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 100005;
ll n, m, a[maxN];
ll block[maxN], lazy[maxN], sum[maxN];
void add(ll l, ll r, ll val) {
if(block[l] == block[r]) {
for(int i = l; i <= r; ++ i ) {
a[i] += val;
}
sum[block[l]] += val * (r - l + 1);
} else {
for(int i = l; i <= block[l] * m; ++ i ) {
a[i] += val;
}
sum[block[l]] += val * (block[l] * m - l + 1);
for(int i = (block[r] - 1) * m + 1; i <= r; ++ i ) {
a[i] += val;
}
sum[block[r]] += val * (r - ((block[r] - 1) * m + 1) + 1);
for(int i = block[l] + 1; i <= block[r] - 1; ++ i ) {
lazy[i] += val;
}
}
}
ll query(ll l, ll r, ll mod) {
ll ans = 0;
if(block[l] == block[r]) {
for(int i = l; i <= r; ++ i ) {
ans += a[i] + lazy[block[l]], ans %= mod;
}
} else {
for(int i = l; i <= block[l] * m; ++ i ) {
ans += a[i] + lazy[block[l]], ans %= mod;
}
for(int i = (block[r] - 1) * m + 1; i <= r; ++ i ) {
ans += a[i] + lazy[block[r]], ans %= mod;
}
for(int i = block[l] + 1; i <= block[r] - 1; ++ i ) {
ans += sum[i] + lazy[i] * m, ans %= mod;
}
}
return ans;
}
int main()
{
n = read(); m = sqrt(n);
for(int i = 1; i <= n; ++ i ) {
a[i] = read();
block[i] = (i - 1) / m + 1;
sum[block[i]] += a[i];
}
for(int i = 1; i <= n; ++ i ) {
ll op, l, r, c;
op = read(), l = read(), r = read(), c = read();
if(!op) { //add
add(l, r, c);
} else { //query
cout << query(l, r, c + 1) << endl;
}
}
return 0;
}
LOJ #6281. 数列分块入门 5
- 题意:给出一个长为n的数列,以及n个操作,操作涉及区间开方,区间求和。
- 思路:开方确实不好搞,但是众所周知分块是优雅的暴力。我们知道2^32最多能开方6次就变成了1,所以我们可以记录一个“块”是否都是1 / 0,如果是的话那么我们不需要再对其进行开方,因为结果不会变了;否则的话我们直接遍历对每个元素进行开方。当然对于非整块我们直接遍历即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll ;
ll read()
{
ll x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 100005;
ll n, m, a[maxN], block[maxN], flag[maxN], sum[maxN];
void SQRT(ll l, ll r) {
if(block[l] == block[r]) {
if(!flag[block[l]]) {
for(int i = l; i <= r; ++ i ) {
sum[block[l]] -= a[i];
a[i] = sqrt(a[i]);
sum[block[l]] += a[i];
}
}
} else {
for(int i = l; i <= block[l] * m; ++ i ) {
sum[block[l]] -= a[i];
a[i] = sqrt(a[i]);
sum[block[l]] += a[i];
}
for(int i = (block[r] - 1) * m + 1; i <= r; ++ i ) {
sum[block[r]] -= a[i];
a[i] = sqrt(a[i]);
sum[block[r]] += a[i];
}
for(int i = block[l] + 1; i <= block[r] - 1; ++ i ) {
if(!flag[i]) {
flag[i] = 1; sum[i] = 0;
for(int j = (i - 1) * m + 1; j <= i * m; ++ j ) {
a[j] = sqrt(a[j]);
sum[i] += a[j];
if(a[j] > 1) flag[i] = 0;
}
}
}
}
}
ll query(ll l, ll r) {
ll ans = 0;
if(block[l] == block[r]) {
for(int i = l; i <= r; ++ i ) {
ans += a[i];
}
} else {
for(int i = l; i <= block[l] * m; ++ i ) {
ans += a[i];
}
for(int i = (block[r] - 1) * m + 1; i <= r; ++ i ) {
ans += a[i];
}
for(int i = block[l] + 1; i <= block[r] - 1; ++ i ) {
ans += sum[i];
}
}
return ans;
}
int main()
{
n = read(); m = sqrt(n);
for(int i = 1; i <= n; ++ i ) {
a[i] = read();
block[i] = (i - 1) / m + 1;
sum[block[i]] += a[i];
}
for(int i = 1; i <= n; ++ i ) {
ll op, l, r, c;
op = read(), l = read(), r = read(), c = read();
if(!op) SQRT(l, r);
else cout << query(l, r) << endl;
}
return 0;
}
LOJ #6282. 数列分块入门 6
- 题意:给出一个长为 n 的数列,以及 n 个操作,操作涉及单点插入,单点询问,数据随机生成。
- 思路:涉及插入操作,所以每个"块"开一个动态数组即可。但是这就涉及到一个问题,如果大量单点插入到一个块中使得该块远远大于
,那么我们对块进行暴力的时候就不能保证复杂度了。所以当一个块中的数目大于
时,我们就对所有数据进行重构,即重新分块,重构的复杂度为O(n),最多重构
次,复杂度还是可以的。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll ;
ll read()
{
ll x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 200005;
ll n, m, newN, up, a[maxN];
vector<ll>vt[maxN];
pair<ll, ll> find(ll pos) {
ll x = 1;
while(pos > vt[x].size()) {
pos -= vt[x ++ ].size();
}
return make_pair(x, pos - 1);
}
void rebuild() {
for(int i = 1; i <= up; ++ i ) {
for(vector<ll>::iterator it = vt[i].begin(); it != vt[i].end(); ++ it ) {
a[++ newN] = *it;
}
vt[i].clear();
}
m = sqrt(newN), up = (newN - 1) / m + 1;
for(int i = 1; i <= newN; ++ i ) {
vt[(i - 1) / m + 1].emplace_back(a[i]);
}
newN = 0;
}
void Insert(ll pos, ll val) {
pair<ll, ll> p = find(pos);
vt[p.first].insert(vt[p.first].begin() + p.second, val);
if(vt[p.first].size() > 2 * m) rebuild();
}
int main()
{
n = read(), m = sqrt(n), up = (n - 1) / m + 1;
for(int i = 1; i <= n; ++ i ) {
a[i] = read();
vt[(i - 1) / m + 1].emplace_back(a[i]);
}
for(int i = 1; i <= n; ++ i ) {
ll op, l, r, c;
op = read(), l = read(), r = read(), c = read();
if(op == 0) { // 第l前插入r
Insert(l, r);
} else { // 查询a[r]
pair<ll, ll> ans = find(r);
cout << vt[ans.first][ans.second] << endl;
}
}
return 0;
}
LOJ #6283. 数列分块入门 7
- 题意:给出一个长为 n 的数列,以及 n 个操作,操作涉及区间乘法,区间加法,单点询问。
- 思路:
- 对于整块,在已经*m, +a的前提下,①+a2:m = m; a = a + a2 ②*m2:m = m * m2; a = a * m2.
- 对于非整块,铁定是不能对部分这个非整块中的元素进行单独的操作的,所以我们需要将该非整块所在的整块之前的lazy标记全都处理掉,然后再单元素处理。【这时候就需要注意块的右端点,因为最后一个块可能不是长度为m的块,取min(n, )即可】
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll ;
ll read()
{
ll x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 100005;
const ll mod = 10007;
ll n, m, a[maxN], block[maxN], lazy_add[maxN], lazy_mul[maxN];
void update(ll x) { //注意块的右端点!!!!
for(int i = (x - 1) * m + 1; i <= min(x * m, n); ++ i ) {
a[i] = a[i] * lazy_mul[x] % mod + lazy_add[x], a[i] %= mod;
}
lazy_mul[x] = 1, lazy_add[x] = 0;
}
void add(ll l, ll r, ll val) {
if(block[l] == block[r]) {
update(block[l]);
for(int i = l; i <= r; ++ i ) {
a[i] += val, a[i] %= mod;
}
} else {
update(block[l]);
for(int i = l; i <= block[l] * m; ++ i ) {
a[i] += val, a[i] %= mod;
}
update(block[r]);
for(int i = (block[r] - 1) * m + 1; i <= r; ++ i ) {
a[i] += val, a[i] %= mod;
}
for(int i = block[l] + 1; i <= block[r] - 1; ++ i ) {
lazy_add[i] += val, lazy_add[i] %= mod;
}
}
}
void mul(ll l, ll r, ll val) {
if(block[l] == block[r]) {
update(block[l]);
for(int i = l; i <= r; ++ i ) {
a[i] *= val, a[i] %= mod;
}
} else {
update(block[l]);
for(int i = l; i <= block[l] * m; ++ i ) {
a[i] *= val, a[i] %= mod;
}
update(block[r]);
for(int i = (block[r] - 1) * m + 1; i <= r; ++ i ) {
a[i] *= val, a[i] %= mod;
}
for(int i = block[l] + 1; i <= block[r] - 1; ++ i ) {
lazy_mul[i] *= val, lazy_mul[i] %= mod;
lazy_add[i] *= val, lazy_add[i] %= mod;
}
}
}
ll query(ll x) {
return (a[x] * lazy_mul[block[x]] % mod + lazy_add[block[x]]) % mod;
}
int main()
{
n = read(); m = sqrt(n);
for(int i = 1; i <= n; ++ i ) {
a[i] = read(), a[i] %= mod;
block[i] = (i - 1) / m + 1;
}
for(int i = 1; i <= block[n]; ++ i ) {
lazy_mul[i] = 1;
}
for(int i = 1; i <= n; ++ i ) {
ll op, l, r, c;
op = read(), l = read(), r = read(), c = read();
if(op == 0) {
add(l, r, c);
} else if(op == 1) {
mul(l, r, c);
} else {
cout << query(r) << endl;
}
}
return 0;
}
LOJ #6284. 数列分块入门 8
- 题意:给出一个长为 n 的数列,以及 n 个操作,操作涉及区间询问等于一个数 c 的元素,并将这个区间的所有元素改为 c 。
- 思路:我们lazy标记一个整块的所有元素是否为相同的值。如果不是那就是-1,如果是就记为块中的元素值。
- 对于整块,如果lazy[]==-1,那么就暴力统计答案;否则O(1)统计答案
- 对于非整块,如果lazy[] == -1,那么暴力统计答案;否则O(1)统计答案。但是要注意:如果lazy[] != -1 && lazy[] != c,那么我们需要将该非整块对应的整块之前的lazy处理掉,然后再给非整块中的元素赋值为c。【其实非整块可以直接全部当作lazy[]==-1来做,只需要将整块的lazy处理掉然后处理非整块中的元素即可,但是显然前面分情况的做法要快一些。】
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll ;
ll read()
{
ll x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 100005;
ll n, m, a[maxN], block[maxN], lazy[maxN];
void update(ll x) {
if(lazy[x] == -1) return ;
for(int i = (x - 1) * m + 1; i <= min(x * m, n); ++ i ) {
a[i] = lazy[x];
}
lazy[x] = -1;
}
void incplet(ll l, ll r, ll val, ll &ans) {
if(lazy[block[l]] == val) {
ans += r - l + 1;
} else if(lazy[block[l]] == -1){
for(int i = l; i <= r; ++ i ) {
if(a[i] == val) {
++ ans;
} else {
a[i] = val;
}
}
} else {
update(block[l]);
for(int i = l; i <= r; ++ i ) {
a[i] = val;
}
}
}
void cplet(ll x, ll val, ll &ans) {
if(lazy[x] == -1) {
for(int i = (x - 1) * m + 1; i <= x * m; ++ i ) {
if(a[i] == val) ++ ans;
else a[i] = val;
}
lazy[x] = val;
} else if(lazy[x] == val) {
ans += m;
} else {
lazy[x] = val;
}
}
ll solve(ll l, ll r, ll val) {
ll ans = 0;
if(block[l] == block[r]) {
incplet(l, r, val, ans);
// update(block[l]);
// for(int i = l; i <= r; ++ i ) {
// if(a[i] == val) ++ ans;
// else a[i] = val;
// }
} else {
incplet(l, block[l] * m, val, ans);
incplet((block[r] - 1) * m + 1, r, val, ans);
// update(block[l]);
// for(int i = l; i <= block[l] * m; ++ i ) {
// if(a[i] == val) ++ ans;
// else a[i] = val;
// }
// update(block[r]);
// for(int i = (block[r] - 1) * m + 1; i <= r; ++ i ) {
// if(a[i] == val) ++ ans;
// else a[i] = val;
// }
for(int i = block[l] + 1; i <= block[r] - 1; ++ i ) {
cplet(i, val, ans);
}
}
return ans;
}
int main()
{
memset(lazy, -1, sizeof(lazy));
n = read(); m = sqrt(n);
for(int i = 1; i <= n; ++ i ) {
a[i] = read();
block[i] = (i - 1) / m + 1;
}
for(int i = 1; i <= n; ++ i ) {
ll l, r, c;
l = read(), r = read(), c = read();
cout << solve(l, r, c) << endl;
}
return 0;
}