算法--分块(更好的暴力)

分块

分块算法就是将一个序列分成若干块,分别进行操作来解决问题。总而言之就是“整块打包维护,碎片逐个枚举”。

基本操作

//block每一块大小   sz 分块数目   pos[i] 第i个数对应的是第几个块
//st[j]第j个块开始的位置 ed[j]第j个块结束的位置	

        block=sqrt(n);   
	sz=n/block;
	if(n%block) sz++;
	for(int i=1;i<=sz;++i) {
		st[i]=(i-1)*block+1;
		ed[i]=i*block;
	}
	ed[sz]=n;
	for(int i=1;i<=n;i++) {
		pos[i]=(i-1)/block+1;
	}

以例题P3372来讲解分块的应用

问题:对一个序列可以进行两种操作 1:将某个区间加上k; 2:求出某个区间的和。

分块解决方案 :

(1)定义数组a[]: 存储原始数据  数组sum[]:表示每一个块的 数组add[]: 表示每一个块加上数

(2)对于[L,R]区间修改: p=pos[L],q=pos[R]。若q=p即修改区间在一个块内,那么直接遍历修改数组a[]和对sum[p]+=d*(R-L+1)。若p!=q,在修改区间完全包含的快上直接对其add进行修改。对不完全包含在区间内的块直接对其sum和a[]修改。

(3)对于区间[L,R]区间求和, p=pos[L],q=pos[R]。若p=q即求和区间在一个块内,直接遍历区间进行求和res=a[L]+...a[R],

在加上add[p]的值,即res+=add[p]*(R-L+1);反之,对完全包含在区间内的快遍历求其和,然后再按照不完全包含的情况逐个遍历求解。

void modify(int d,int L,int R) {
    int p = pos[L], q = pos[R];
    if(p==q) {                      
       for(int i=L;i<=R;i++) a[i]+=d;
       sum[p]+=d*(R-L+1);
    } else {                        
       for(int i=p+1;i<=q-1;i++) add[i]+=d; 
       for(int i=L;i<=ed[p];i++) a[i]+=d;   
       sum[p]+=d*(ed[p]-L+1);
       for( int i=st[q];i<=R;i++) a[i]+=d;   
       sum[q]+=d*(R-st[q]+1);
    }
 }
ll query(int L,int R) {
    int p = pos[L], q = pos[R];
    ll res=0;
    if(p==q){                    
       for(int i=L;i<=R;i++) res+=a[i];
       res+=add[p]*(R-L+1);
    }
    else{
       for(int i=p+1;i<=q-1;i++)  res+=sum[i]+add[i]*(ed[i]-st[i]+1);//整块
       //部分块
       for(int i=L;i<=ed[p];i++)  res+=a[i];   
       res+=add[p]*(ed[p]-L+1);
       for(int i=st[q];i<=R;i++)  res+=a[i]; 
       res+=add[q]*(R-st[q]+1);
    }
    return res;
 }

猜你喜欢

转载自blog.csdn.net/qq_44132777/article/details/108818030
今日推荐