zoukankan      html  css  js  c++  java
  • 分块入门(线段树区间修改)

    例题地址:嘟嘟嘟

    分块其实我早都听说过,而且怎么回事差不多都清楚了,只是一直没有写。现在离NOIP2018也挺近了,考虑到分块有时候确实能水到不少的分,决定这几天写一写。

    众所周知,分块就是对于一个询问区间[L, R],属于刚好一个块中的部分就整块处理,多出来的散的部分就暴力处理。令块大小为S,则共有⌊n / S⌋块,每一次操作多出来的部分不会超过2 * S个,那么最坏复杂度是O(⌊n / S⌋ + 2 * S) 。为了均衡,就取了S = √n。

    对于这道题,还是要以一个表示数组add,但这个标记没那么复杂,不用下传,只是为了询问单个元素的时候用。具体操作是这样的:

    1.修改:对于区间[L, R],令l = L / S + 1, r = R / S + 1。那么 l + 1块到 r - 1块一定是整块,直接修改sum数组。对于前面的零散部分,即[L, l * S - 1](后面有证明为啥是这个)和后面的[(r - 1) * S, R],就直接修改a[i]。

    2.询问:和修改极其像。对于整块的,ret += sum[i] (i 此时表示第几个块);对于单个的,ret += a[i] + add[k](k = i / S + 1)。

    上面的证明:只要求出来第k个块所在区间即可:因为⌊i / S⌋ + 1 = k,所以⌊i / S⌋ = k - 1,则 i ∈ [(k - 1) * S, (k - 1) * S + S - 1] => i ∈ [(k - 1) * S, k * S - 1]。那么[L, R]中前半部分就是[L, l * S - 1],后半部分就是[(r - 1) * S, R]。

    上代码

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cstring>
     6 #include<cstdlib>
     7 #include<cctype>
     8 #include<vector>
     9 #include<stack>
    10 #include<queue>
    11 using namespace std;
    12 #define enter puts("") 
    13 #define space putchar(' ')
    14 #define Mem(a, x) memset(a, x, sizeof(a))
    15 #define rg register
    16 typedef long long ll;
    17 typedef double db;
    18 const int INF = 0x3f3f3f3f;
    19 const db eps = 1e-8;
    20 const int maxn = 1e5 + 5;
    21 inline ll read()
    22 {
    23     ll ans = 0;
    24     char ch = getchar(), last = ' ';
    25     while(!isdigit(ch)) {last = ch; ch = getchar();}
    26     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
    27     if(last == '-') ans = -ans;
    28     return ans;
    29 }
    30 inline void write(ll x)
    31 {
    32     if(x < 0) x = -x, putchar('-');
    33     if(x >= 10) write(x / 10);
    34     putchar(x % 10 + '0');
    35 }
    36 
    37 int n, m, S;
    38 
    39 int bel(int x)
    40 {
    41     return x / S + 1;
    42 }
    43 ll a[maxn], sum[maxn], add[maxn];
    44 void update(int L, int R, int k)
    45 {
    46     int l = bel(L), r = bel(R);
    47     if(l == r)        //别忘不够一个块的情况 
    48     {
    49         for(int i = L; i <= R; ++i) a[i] += k, sum[bel(L)] += k;
    50         return;
    51     }
    52     for(int i = l + 1; i < r; ++i) sum[i] += k * S, add[i] += k;
    53     for(int i = L; i <= l * S - 1; ++i) a[i] += k, sum[bel(i)] += k;
    54     for(int i = (r - 1) * S; i <= R; ++i) a[i] += k, sum[bel(i)] += k;
    55 }
    56 ll query(int L, int R)
    57 {
    58     ll ret = 0;
    59     int l = bel(L), r = bel(R);
    60     if(l == r)
    61     {
    62         for(int i = L; i <= R; ++i) ret += a[i] + add[l];
    63         return ret;
    64     }
    65     for(int i = l + 1; i < r; ++i) ret += sum[i];
    66     for(int i = L; i <= l * S - 1; ++i) ret += a[i] + add[bel(i)];
    67     for(int i = (r - 1) * S; i <= R; ++i) ret += a[i] + add[bel(i)];
    68     return ret;
    69 }
    70 
    71 int main()
    72 {
    73     n = read(); m = read(); S = sqrt(n);
    74     for(int i = 1; i <= n; ++i) a[i] = read(), sum[bel(i)] += a[i];
    75     for(int i = 1; i <= m; ++i)
    76     {
    77         int d = read(), L = read(), R = read();
    78         if(d == 1) 
    79         {
    80             ll k = read();
    81             update(L, R, k);
    82         }
    83         else write(query(L, R)), enter;
    84     }
    85     return 0;
    86 }
    View Code
  • 相关阅读:
    学习制作操作系统 0
    阅读《C陷阱与缺陷》的知识增量
    CSS 优先级和特指度
    openCV2马拉松第19圈——Harris角点检測(自己实现)
    Cacti监控mysql数据库server实现过程
    ledisdb:支持类redis接口的嵌入式nosql
    03005_SQL查询语句
    通过smtp直接发送邮件
    XML 解析默认去掉命名空间和注释
    C# /VB.NET 创建PDF项目符号列表和多级编号列表
  • 原文地址:https://www.cnblogs.com/mrclr/p/9863833.html
Copyright © 2011-2022 走看看