zoukankan      html  css  js  c++  java
  • 分块的总结

    分块,是一种优雅的暴力,它通过对数列分段,完成对数列一些区间操作和区间查询的操作,是一种根号算法。

    分块的功能:

        1.区间加;2.区间减;3.。。。。。。

        4.查询区间和 3.查询任意区间内有多少个数大于等于k(注意,这个功能是我们使用分块而不选择线段树的重要依据)

    在我的分块中,block表示原数组被分块后每块有多少个元素(不算最后一个);

                              l[i]表示第i块的最左元素在原数组的位置;r[i]则表示最右面的;

                             belong[i]表示原数组的第i个位置的元素属于第几个块;

                             tot则表示一共有多少个块;

    一.建立块:

      正常情况下:block=sqrt(n);块的数量是n/block;。但是n不一定是一个完全平方数,剩余的部分我们要特殊处理

    tot=n/block;
    if(n%block)
        tot++;

    然后l[i]=(i-1)*block+1; r[i]=i*block; r[tot]=n;

    for(int i=1;i<=tot;i++){
        L[i]=(i-1)*block+1;
        R[i]=i*block;
    }
    R[tot]=n;

    belong[i]=(i-1)/block+1;

    for(int i=1;i<=n;i++)
        belong[i]=(i-1)/block+1;

    这项工作因题目不同而不同,如【教主的魔法】一题,就要对每个块的元素进行排序。

    因为排序会对原始数列作出改变,所以在本题中,应当先把数列复制一遍再进行分块

    二.修改:

       在修改操作中,如果是整块,就不维护每个的具体信息,而是在这个块的lazy上进行操作;而对于并不覆盖整个区间的操作的剩余部分,我们使用暴力进行修改;

     特别的,我们需要特判belong[x]==belong[r];

    void change(int x,int y,int k)
    {
    	if(belong[x]==belong[y]){
    		for(register int i=x;i<=y;i++){
    			a[i]+=k;
    		}
    		for(register int i=l[belong[x]];i<=r[belong[x]];i++){
    			b[i]=a[i];
    		}
    		sort(b+l[belong[x]],b+r[belong[x]]+1);
    		return ;
    	}
    	for(register int i=x;i<=r[belong[x]];i++){
    		a[i]+=k;
    	}
    	for(register int i=l[belong[x]];i<=r[belong[x]];i++){
    		b[i]=a[i];
    	}
    	sort(b+l[belong[x]],b+r[belong[x]]+1);
    	for(register int i=l[belong[y]];i<=y;i++){
    		a[i]+=k;
    	}
    	for(register int i=l[belong[y]];i<=r[belong[y]];i++){
    		b[i]=a[i];
    	}
    	sort(b+l[belong[y]],b+r[belong[y]]+1);
    	for(register int i=belong[x]+1;i<=belong[y]-1;i++){
    		lazy[i]+=k;
    	}
    }
    

    三.查询:

        和修改的思想一样:能块上修改就修改,否则就暴力修改;

        特别的,我们需要特判belong[x]==belong[r];

    int query(int x,int y,int goal)
    {
    	int ans=0;
    	if(belong[x]==belong[y]){
    		for(register int i=x;i<=y;i++){
    			if(a[i]+lazy[belong[x]]>=goal) ++ans;
    		}
    		return ans;
    	}
    	for(register int i=x;i<=r[belong[x]];i++){
    		if(a[i]+lazy[belong[x]]>=goal) ++ans;
    	}
    	for(register int i=l[belong[y]];i<=y;i++){
    		if(a[i]+lazy[belong[y]]>=goal) ++ans;
    	}
    	for(register int i=belong[x]+1;i<=belong[y]-1;i++){
    		int L=l[i],R=r[i],mid;
    		while(L<R){
    			int mid=(L+R)/2;
    			if(b[mid]+lazy[i]>=goal){
    				R=mid;
    			}
    			else{
    				L=mid+1;
    			}
    		}
    		if(L==r[i]){
    			ans+=((b[L]+lazy[i])>=goal);
    			continue;
    		}
    		ans+=(r[i]-L+1);
    	}
    	return ans;
    } 
    

    分块的思想并不难,难就难在对于代码的调试和细节;

    尤其是每个数组的含义:每个数组的下标表示的是原数组的下标还是分块后块的下标;这我在下面的代码中已经注明;

    接下来用本人A掉的第一道分块题来结束这篇文章;(教主的魔法)

    #include <bits/stdc++.h>
    #pragma GCC optimize(2)
    using namespace std;
    int a[1000010],b[1000010],belong[1000010]; //原来的 
    int lazy[1000010],l[1000010],r[1000010]; //块上的 
    int block,tot,n,m;
    void build()
    {
    	block=sqrt(n);
    	tot=n/block;
    	if(n%block){
    		++tot;
    	}
    	for(register int i=1;i<=n;i++){
    		b[i]=a[i];
    		belong[i]=(i-1)/block+1;
    	}
    	for(register int i=1;i<=tot;i++){
    		l[i]=(i-1)*block+1;
    		r[i]=i*block;
    	}
    	r[tot]=n;
    	for(register int i=1;i<=tot;i++){
    		sort(b+l[i],b+r[i]+1);
    	}
    	return;
    }
    void change(int x,int y,int k)
    {
    	if(belong[x]==belong[y]){
    		for(register int i=x;i<=y;i++){
    			a[i]+=k;
    		}
    		for(register int i=l[belong[x]];i<=r[belong[x]];i++){
    			b[i]=a[i];
    		}
    		sort(b+l[belong[x]],b+r[belong[x]]+1);
    		return ;
    	}
    	for(register int i=x;i<=r[belong[x]];i++){
    		a[i]+=k;
    	}
    	for(register int i=l[belong[x]];i<=r[belong[x]];i++){
    		b[i]=a[i];
    	}
    	sort(b+l[belong[x]],b+r[belong[x]]+1);
    	for(register int i=l[belong[y]];i<=y;i++){
    		a[i]+=k;
    	}
    	for(register int i=l[belong[y]];i<=r[belong[y]];i++){
    		b[i]=a[i];
    	}
    	sort(b+l[belong[y]],b+r[belong[y]]+1);
    	for(register int i=belong[x]+1;i<=belong[y]-1;i++){
    		lazy[i]+=k;
    	}
    }
    int query(int x,int y,int goal)
    {
    	int ans=0;
    	if(belong[x]==belong[y]){
    		for(register int i=x;i<=y;i++){
    			if(a[i]+lazy[belong[x]]>=goal) ++ans;
    		}
    		return ans;
    	}
    	for(register int i=x;i<=r[belong[x]];i++){
    		if(a[i]+lazy[belong[x]]>=goal) ++ans;
    	}
    	for(register int i=l[belong[y]];i<=y;i++){
    		if(a[i]+lazy[belong[y]]>=goal) ++ans;
    	}
    	for(register int i=belong[x]+1;i<=belong[y]-1;i++){
    		int L=l[i],R=r[i],mid;
    		while(L<R){
    			int mid=(L+R)/2;
    			if(b[mid]+lazy[i]>=goal){
    				R=mid;
    			}
    			else{
    				L=mid+1;
    			}
    		}
    		if(L==r[i]){
    			ans+=((b[L]+lazy[i])>=goal);
    			continue;
    		}
    		ans+=(r[i]-L+1);
    	}
    	return ans;
    } 
    int main()
    {
    	cin>>n>>m;
    	for(register int i=1;i<=n;i++) scanf("%d",&a[i]);
    	build();
    	for(register int i=1;i<=m;i++){
    		char type;
    		int x,y,k;
    		cin>>type>>x>>y>>k;
    		if(type=='M'){
    			change(x,y,k);
    		}
    		else{
    			cout<<query(x,y,k)<<endl;
    		}
    	}
    } 
    
  • 相关阅读:
    编程实现折半法查找
    浅谈C++多态性
    纯虚函数的使用汇总
    虚函数如何实现多态 ?
    重载(overload),覆盖(override),隐藏(hide)的区别
    Qt入门之常用Qt标准对话框之QMessageBox
    Qt5学习笔记(5)——列表框QListWidget类
    python 文件的方法
    python---while循环
    python ---strip()方法,split()方法,删除字符串开头或结尾,字符串分隔
  • 原文地址:https://www.cnblogs.com/kamimxr/p/11600268.html
Copyright © 2011-2022 走看看