About 分块

分块的的复杂度是带根号的。。。。

然后,它是一种暴力算法

简单来说就是优化过的暴力

分块算法会对一个序列(长度为N)进行划分,每一块最多有K个元素,这样就会分为N/K块;

一般K取sqrt(N),那么块数也有K+1 or K块

通常实现时,我们用belong【i】示第i个位置所属的块,对于每个块都进行信息维护。

单点修改时,我们一般先将对应块的标记下传,再暴力更新被修改块的状态

如果是区间 l,r 修改的话,对于被整块跨过的块直接打标记,两端剩余的部分暴力重构

至于询问操作,和区间修改类似,对于中间跨过的整块,直接利用块保存的信息统计答案,两端剩余部分打暴力

m次询问复杂度o(sqrt(N))

以上就是思路;

建块:

void divide()
{
     //n 个数
    int s=sqrt(n);//分成块的大小为sqrt(n);
    for(int i=1;i<=n;i++)
        belong[i]=(i-1)/s+1;//belong[i]数组表示第i个数所在的块
    int cnt=belong[n];//一共有几块

    for(int i=1;i<=cnt;i++) 
    { //设置每一块长度
        sum[i]=add[i]=0;//sum表示此时第i块的总值为几,add表示第i块添加的数的总和
        if(i==cnt) 
        {
            //防止最后一块不足s,size表示每一块的长度
            if (n%s==0) size[i]=s;
            else size[i]=n%s;
        } 
        else size[i]=s;
    }
    for(int i=1;i<=n;i++)
        sum[belong[i]]+=a[i];//i所属的那一块的sum加上ai

}

修改:

void modify(int l,int r,int v) 
{
    //修改
    if (belong[l]==belong[r]) 
    {
        //属于同一分块
        for (int i=l; i<=r; i++)so easy
            a[i]+=v;
        return;
    }
    while (belong[l]==belong[l-1]) 
    {
        //自此往下同上访问
        a[l]+=v;
        l++;
    }
    while (belong[r]==belong[r+1]) 
    {
        a[r]+=v;
        r--;
    }
    for (int i=belong[l]; i<=belong[r]; i++) {
        add[i]+=v;
        sum[i]+=size[i]*v;
    }
}

查询:

int query(int l,int r) 
{
    //访问
    int ans=0;
    if(belong[l]==belong[r]) ans-=sum[belong[l]];//此处为避免l与r属于同一块画图很好理解,此处不再解释
    while (belong[l]==belong[l-1]) 
    {
        ans+=a[l]+add[belong[l]];//因为l要++,所以l会遍历在从l到r的不整区间中所有的数,so要加a数组
        //**从左到第一个整区间
        l++;
    }
    while(belong[r]==belong[r+1]) 
    {
        //**从最后一个整区间到最后
        ans+=a[r]+add[belong[r]];
        r--;
    }
    for (int i=belong[l]; i<=belong[r]; i++) //加入所有整区间的数
        ans+=sum[i];
    return ans;
}
原文地址:https://www.cnblogs.com/sssy/p/6878778.html