【分块入门】LOJ 数列分块入门 1 - 9 (学习更新……)

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

分块

  • 同样的,我们将整个区间划分为\sqrt{n}个区间,每个区间有\sqrt{n}个元素。
  • 对于查询操作:因为我们要找区间内小于某个值x的元素,那我们就想了,如果每个块都是有序的,那么我们直接lower_bound就O(\sqrt{n}log\sqrt{n})找到“整块”中满足条件的个数。于是我们想到要给划分的块块内排序。所有块排序复杂度:O(\sqrt{n}\sqrt{n}log\sqrt{n})
  • 那么对于“非完整块”,我们还是直接暴力查找。O(\sqrt{n})
  • 对于修改操作:我们需要考虑,进行加的操作的时候,可以会改变“块”内的有序性。所以我们我们需要对这些“非完整块”进行重新排序
#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;
}

类似题:HDU 4417 Super Mario


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 个操作,操作涉及单点插入,单点询问,数据随机生成。
  • 思路:涉及插入操作,所以每个"块"开一个动态数组即可。但是这就涉及到一个问题,如果大量单点插入到一个块中使得该块远远大于\sqrt{n},那么我们对块进行暴力的时候就不能保证复杂度了。所以当一个块中的数目大于2\sqrt{n}时,我们就对所有数据进行重构,即重新分块,重构的复杂度为O(n),最多重构\sqrt{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 个操作,操作涉及区间乘法,区间加法,单点询问。
  • 思路:
  1. 对于整块,在已经*m, +a的前提下,①+a2:m = m; a = a + a2 ②*m2:m = m * m2; a = a * m2.
  2. 对于非整块,铁定是不能对部分这个非整块中的元素进行单独的操作的,所以我们需要将该非整块所在的整块之前的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,如果是就记为块中的元素值。
  1. 对于整块,如果lazy[]==-1,那么就暴力统计答案;否则O(1)统计答案
  2. 对于非整块,如果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;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值