分块

分块


思想:将长区间分为小区间,维护区间值,降低时间复杂度
通常将区间分为 n \sqrt{n} n 块,这样可以保证块数以及块数内的元素都较少,对区间內部的整块进行整体的操作,对区间边缘的零散块单独暴力处理。

时间复杂度: O ( n ) O(\sqrt{n}) O(n )

具体实现:

预处理

const int N = 1e5 +5, M = 500;
int arr[N], sq, n, len;
int st[M], ed[M], pos[N], tag[M];
vector<int> block[M];  //划分的块里的数字

划分块

void build(){
    sq = sqrt(n);                   //sq:分的块数
    len = n / sq;                   //寻常块的长度
    for (int i = 1; i <= sq; i++){
        st[i] = len * (i - 1) + 1;  //第i块的开始下标
        ed[i] = len * i;            //第i块的结束下标
    }
    ed[sq] = n;                      //将最后剩余放入最后一块中
    for (int i = 1; i <= sq; i++){
        for (int j = st[i]; j <= ed[i]; j++){
            pos[j] = i;                 //代表j属于第i块
            block[i].push_back(arr[j]); //将该数字放入第i块中
        }
    }
    for (int i = 1; i <= sq; i++){     //对块内数据排序,对于需要二分题需要
        sort(block[i].begin(), block[i].end());
    }
}

如果对于块内数据需要排序,可用到更新块内数据函数

void reset(int x)
{
    block[x].clear();
    for (int i = st[x]; i <= ed[x]; i++){
        block[x].push_back(arr[i]);
    }
    sort(block[x].begin(), block[x].end());

}

( l , r ) (l,r) (l,r)中的数据增加c

void add(int l, int r, int c){
    if (pos[l] == pos[r]){              //数据在同一块内
        for (int i = l; i <= r; i++){
            arr[i] += c;
        }
        reset(pos[l]);                   //重新排列打乱顺序的块
    }
    else{
        for (int i = l; i <= ed[pos[l]]; i++){  //左零散暴力
            arr[i] += c;
        }
        reset(pos[l]);
        for (int i = st[pos[r]]; i <= r; i++){  //右零散暴力
            arr[i] += c;
        }
        reset(pos[r]);
        for (int i = pos[l] + 1; i < pos[r]; i++){ //整块打上标记
            tag[i] += c;
        }
    }
}

总体思想:将一个大区间划分成小区间,求区间问题时可以对区间进行总体操作,对两遍的零散数据进行暴力操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值