zoukankan      html  css  js  c++  java
  • 【模板篇】树状数组(六)

    上次我们聊到了二维树状数组的 单点加 区间查.
    那么我们yy一下区间加 单点查也不难.
    我们就让树状数组维护差分数组, 只不过是变成了二维差分, 都差不多的.
    我们可以仿照一维的 改写成这样

    int c[N][N],n,m;
    void add(int x,int y,int s){
    	for(int i=x;i<=n;i+=i&-i)
    		for(int j=y;j<=m;j+=j&-j)
    			c[i][j]+=s;
    }
    int query(int x,int y,int s=0){
    	for(int i=x;i;i-=i&-i)
    		for(int j=y;j;j-=i&-j)
    			s+=c[i][j];
    }
    void Add(int x1,int y1,int x2,int y2,int s){
    	add(x1,y1,s); add(x1,y2+1,-s); add(x2+1,y1,-s); add(x2+1,y2+1,s);
    }
    

    而其实二维树状数组也可以做到区间加, 区间查的.
    而且据说线段树套线段树是没法维护的....
    我们看一道板子题: 上帝造题的七分钟

    那怎么做呢? 我们还是仿照一维的推柿子.
    不过今天我按照之前的方法推柿子感觉并不好推, 于是获取了一种新的推法.
    由于我们维护的差分数组的前缀和才是单点的值, 也就是说我们计算(a_i)时, (c_1sim c_i)每个值都会被计算一次.
    这样我们计算(sum_{i=1}^na_i)的时候,(c_1)应该会出现(n)次, (c_2)应该会出现(n-1)次, ... , (c_k)会出现(n-k+1)次, ... ,(c_n)会出现一次. 所以

    [ans=sum_{i=1}^na_i=sum_{i=1}^nsum_{j=1}^ic_j=sum_{i=1}^nc_i*(n-i+1)=sum_{i=1}^nc_i*(n+1)-sum_{i=1}^nc_i*i ]

    这样我们用两个树状数组分别维护(c_i)的和和(c_i*i)的和即可.
    而之前我们得出的结论也不是错的, 如果我们这么拆

    [ans=sum_{i=1}^nc_i*(n-i+1)=sum_{i=1}^nc_i*n+sum_{i=1}^nc_i*(i-1) ]

    就变成了维护(c_I)(c_i*(i-1)), 最后结果应该是一样的, 具体用哪种看个人喜好吧... (似乎前面那种用起来比较舒服..)


    情况扩展到了二维, 除了二维差分复杂了一(hen)些(duo)以外, 原理上是一样的.
    我们依然用(c_{i,j})维护差分数组, 所以显然(sum_{i=1}^nsum_{j=1}^mc_{i,j}=a_{n,m})
    然后仿照一维的, 把一个查询拆成四个, 对于每个查询,

    [ans=sum_{i=1}^nsum_{j=1}^msum_{x=1}^isum_{y=1}^jc_{i.j} ]

    这里面很显然每个(c_i,j)被使用的次数等于((i,j))为左上角,((n,m))为右下角的矩形面积(不信你可以画个图试试), 也就是((n-i+1)*(m-j+1)), 所以

    [ans=sum_{i=1}^nsum_{j=1}^msum_{x=1}^isum_{y=1}^jc_{i.j}=sum_{i=1}^nsum_{j=1}^mc_{i.j}*(n-i+1)*(m-j+1) \ =sum_{i=1}^nsum_{j=1}^mc_{i,j}*(n+1)*(m+1)-sum_{i=1}^nsum_{j=1}^mc_{i,j}*i*(m+1)-sum_{i=1}^nsum_{j=1}^mc_{i,j}*j*(n+1)+sum_{i=1}^nsum_{j=1}^mc_{i,j}*i*j ]

    所以按照套路我们只需要开四个树状数组分别维护(c_{i,j},c_{i,j}*i,c_{i,j}*j,c_{i,j}*i*j)就行了..

    不管是区间加还是区间查都巨繁琐qwq...
    下面给出代码;

    #include <cstdio>
    inline int gn(int a=0,char c=0,int f=1){
    	for(;(c<'0'||c>'9')&&c!='-';c=getchar());
    	if(c=='-') c=getchar(),f=-1;
    	for(;c>47&&c<58;c=getchar()) a=a*10+c-48;
    return a*f;}
    struct BIT{
    	int c[2050][2050],n,m;
    	
    	void add(int x,int y,int s){
    		for(int i=x;i<=n;i+=i&-i)
    			for(int j=y;j<=m;j+=j&-j)
    				c[i][j]+=s;
    	}
    	
    	int query(int x,int y,int s=0){
    		for(int i=x;i;i-=i&-i)
    			for(int j=y;j;j-=j&-j)
    				s+=c[i][j];
    		return s;
    	}
    	
    }a,b,c,d;
    
    void Add(int x,int y,int s){
    	a.add(x,y,s); b.add(x,y,s*x); c.add(x,y,s*y); d.add(x,y,s*x*y);
    }
    int Query(int x,int y){
    	return a.query(x,y)*(x+1)*(y+1)-b.query(x,y)*(y+1)-c.query(x,y)*(x+1)+d.query(x,y); 
    }
    
    int main(){
    	int n=gn(),m=gn(); char opt[4];
    	a.n=b.n=c.n=d.n=n; a.m=b.m=c.m=d.m=m;
    	while(~scanf("%s",opt)){
    		if(opt[0]=='L'){
    			int x1=gn(),y1=gn(),x2=gn(),y2=gn(),s=gn();
    			Add(x1,y1,s); Add(x1,y2+1,-s); Add(x2+1,y1,-s); Add(x2+1,y2+1,s); 
    		}
    		else{
    			int x1=gn(),y1=gn(),x2=gn(),y2=gn();
    			printf("%d
    ",Query(x2,y2)-Query(x1-1,y2)-Query(x2,y1-1)+Query(x1-1,y1-1));
    		}
    	}
    } 
    
  • 相关阅读:
    Linux添加用户组和删除用户组
    购物意图分析
    架构是什么来的
    如何突破浏览器加载并发数的限制
    写JS自执行函数时要注意的
    网页是什么
    JVM
    javascript的边界
    浏览器
    HTTP
  • 原文地址:https://www.cnblogs.com/enzymii/p/8967957.html
Copyright © 2011-2022 走看看