ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【洛谷】P2801 教主的魔法

2021-07-20 09:57:54  阅读:197  来源: 互联网

标签:洛谷 log get int 整块 P2801 魔法 ++ 1000


题目地址:

https://www.luogu.com.cn/problem/P2801

题目描述:
教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是 N N N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为 1 , 2 , … , N 1, 2, \ldots, N 1,2,…,N。每个人的身高一开始都是不超过 10001000 10001000 10001000的正整数。教主的魔法每次可以把闭区间 [ L , R ] [L, R] [L,R]( 1 ≤ L ≤ R ≤ N 1≤L≤R≤N 1≤L≤R≤N)内的英雄的身高全部加上一个整数 W W W。(虽然 L = R L=R L=R时并不符合区间的书写规范,但我们可以认为是单独增加第 L ( R ) L(R) L(R)个英雄的身高)CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [ L , R ] [L, R] [L,R]内有多少英雄身高大于等于 C C C,以验证教主的魔法是否真的有效。WD巨懒,于是他把这个回答的任务交给了你。

输入格式:
第 1 1 1行为两个整数 N , Q N, Q N,Q。 Q Q Q为问题数与教主的施法数总和。
第 2 2 2行有 N N N个正整数,第 i i i个数代表第 i i i个英雄的身高。
第 3 3 3到第 Q + 2 Q+2 Q+2行每行有一个操作:若第一个字母为 M M M,则紧接着有三个数字 L , R , W L, R, W L,R,W。表示对闭区间 [ L , R ] [L, R] [L,R]内所有英雄的身高加上 W W W。若第一个字母为 A A A,则紧接着有三个数字 L , R , C L, R, C L,R,C。询问闭区间 [ L , R ] [L, R] [L,R]内有多少英雄的身高大于等于 C C C。

输出格式:
对每个 A A A询问输出一行,仅含一个整数,表示闭区间 [ L , R ] [L, R] [L,R]内身高大于等于 C C C的英雄数。

数据范围:
对于 30 % 30\% 30%的数据, N ≤ 1000 N≤1000 N≤1000, Q ≤ 1000 Q≤1000 Q≤1000。对于 100 % 100\% 100%的数据, N ≤ 1 0 6 N≤10^6 N≤106, Q ≤ 3000 Q≤3000 Q≤3000, 1 ≤ W ≤ 1000 1≤W≤1000 1≤W≤1000, 1 ≤ C ≤ 1 0 9 1≤C≤10^9 1≤C≤109。

其实是要实现一个数据结构可以做两种操作,一个是区间增加某个数 x x x,另一个是查询区间里大于等于某个数的个数有多少个。可以用分块的思想来做。将 [ l , r ] [l,r] [l,r]里的数增加 v v v,这个操作可以用分块 + 懒标记来做,但是求 [ l , r ] [l,r] [l,r]里大于等于 v v v的数的个数,这个操作光靠分块是不行的,可以将每个块都存下来并保持有序,这样整块内就可以二分来做,在散块里的还需要暴力求解。综上,两个操作我们这样做:
1、初始化的时候,开个vector向量 v v v, v [ i ] v[i] v[i]存第 i i i个分块里的所有数,并排好序( v [ i ] v[i] v[i]本身是个vector);
2、区间 [ l , r ] [l,r] [l,r]增加 v v v的操作,如果区间属于一个整块,则直接暴力做;对于在散块里的数,也直接暴力做。这两种情况下做完了要将 v [ i ] v[i] v[i]更新并排序;而对于整块里的数,只做懒标记;
3、区间 [ l , r ] [l,r] [l,r]里查询大于等于 v v v的数的个数,如果区间属于一个整块,或者对于在散块里的数,这两种情况都是暴力计数(注意要加懒标记);对于在整块里的数,由于整块里的数在 v [ i ] v[i] v[i]里都排好序了,所以可以直接二分来计数(也要加懒标记)。

代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;

const int N = 1e6 + 10, M = 1050;
int n, q, len;
int w[N], lazy[M];
vector<int> BQ[M];

// 得到i是属于第几个分块
int get(int i) {
    return i / len;
}

void init() {
    for (int i = 0; i < n; i++) BQ[get(i)].push_back(w[i]);
    for (int i = 0; i < n; i += len) sort(BQ[get(i)].begin(), BQ[get(i)].end());
}

// 更新第u个分块
void update(int u) {
    vector<int> &v = BQ[u];
    // 清空后把数重新加进去并排序
    v.clear();
    for (int i = u * len; i < min(n, (u + 1) * len); i++)
        v.push_back(w[i]);

    sort(v.begin(), v.end());
}

// 将区间[l, r]中每个数增加v
void add(int l, int r, int v) {
    if (get(l) == get(r)) {
    	// 属于同一个整块,暴力做完后更新这个整块
        for (int i = l; i <= r; i++) w[i] += v;
        update(get(l));
    } else {
    	// 先将散块暴力更新然后更新块的信息
        int i = l, j = r;
        while (get(i) == get(l)) w[i++] += v;
        update(get(l));
        while (get(j) == get(r)) w[j--] += v;
        update(get(r));
        // 对于整块只做懒标记即可
        for (int k = get(i); k <= get(j); k++) lazy[k] += v;
    }
}

// 查询[l, r]里大于等于v的数的个数
int query(int l, int r, int v) {
    int res = 0;
    if (get(l) == get(r)) {
    	// 如果在一个整块里,则直接暴力计数
        for (int i = l; i <= r; i++)
            if (w[i] + lazy[get(l)] >= v) res++;
    } else {
    	// 否则先暴力计数在散块里的数
        int i = l, j = r;
        while (get(i) == get(l)) {
            if (w[i] + lazy[get(i)] >= v) res++;
            i++;
        }
        while (get(j) == get(r)) {
            if (w[j] + lazy[get(j)] >= v) res++;
            j--;
        }
        // 对于整块,由于整块已经都排好序了,所以可以直接二分求解
        for (int k = get(i); k <= get(j); k++) {
            vector<int> &vec = BQ[k];
            int l = 0, r = vec.size() - 1;
            while (l < r) {
                int mid = l + (r - l >> 1);
                if (vec[mid] + lazy[k] >= v) r = mid;
                else l = mid + 1;
            }

            if (vec[l] + lazy[k] >= v) res += vec.size() - l;
        }
    }

    return res;
}

int main() {
    scanf("%d%d", &n, &q);
    len = sqrt(n);
    for (int i = 0; i < n; i++) scanf("%d", &w[i]);

    init();

    while (q--) {
        char op[2];
        int l, r, v;
        scanf("%s%d%d%d", op, &l, &r, &v);
        if (op[0] == 'A') printf("%d\n", query(l - 1, r - 1, v));
        else add(l - 1, r - 1, v);
    }

    return 0;
}

初始化时间复杂度 O ( N log ⁡ N ) = O ( N log ⁡ N ) O(N {\log \sqrt N})=O(N\log N) O(NlogN ​)=O(NlogN),每次操作时间复杂度 O ( N log ⁡ N ) = O ( N log ⁡ N ) O(\sqrt N\log \sqrt N)=O(\sqrt N\log N) O(N ​logN ​)=O(N ​logN),空间 O ( N ) O(N) O(N)。

标签:洛谷,log,get,int,整块,P2801,魔法,++,1000
来源: https://blog.csdn.net/qq_46105170/article/details/118925878

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有