zoukankan      html  css  js  c++  java
  • [洛谷P4514] 上帝造题的七分钟

    Description

    “第一分钟,X说,要有矩阵,于是便有了一个里面写满了 (0)(n imes m) 矩阵。
    第二分钟,L说,要能修改,于是便有了将左上角为 ((a,b)),右下角为 ((c,d)) 的一个矩形区域内的全部数字加上一个值的操作。
    第三分钟,k说,要能查询,于是便有了求给定矩形区域内的全部数字和的操作。
    第四分钟,彩虹喵说,要基于二叉树的数据结构,于是便有了数据范围。
    第五分钟,和雪说,要有耐心,于是便有了时间限制。
    第六分钟,吃钢琴男说,要省点事,于是便有了保证运算过程中及最终结果均不超过32位有符号整数类型的表示范围的限制。
    第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。”
    ——《上帝造裸题的七分钟》
    所以这个神圣的任务就交给你了。

    Input

    输入数据的第一行为 (X) (n) (m),代表矩阵大小为 (n imes m)
    从输入数据的第二行开始到文件尾的每一行会出现以下两种操作:

    (L) (a) (b) (c) (d) (delta) —— 代表将 ((a,b)),((c,d)) 为顶点的矩形区域内的所有数字加上 (delta)
    (k) (a) (b) (c) (d) —— 代表求 ((a,b),(c,d)) 为顶点的矩形区域内所有数字的和。
    请注意,(k) 为小写。

    Output

    针对每个 (k) 操作,在单独的一行输出答案。

    Sample Input

    X 4 4
    L 1 1 3 3 2
    L 2 2 4 4 1
    k 2 2 3 3

    Sample Output

    12

    HINT

    对于 (10\%) 的数据,(1 leq n leq 16, 1 leq m leq 16) , 操作不超过200个.
    对于 (60\%) 的数据,(1 leq n leq 512, 1 leq m leq 512).
    对于 (100\%) 的数据,(1 leq n leq 2048, 1 leq m leq 2048, -500 leq delta leq 500) ,操作不超过200000个,保证运算过程中及最终结果均不超过32位带符号整数类型的表示范围。


    想法

    挺“模板”的题,但感觉思想很巧妙。
    二维树状数组。

    先考虑一维的情况,则题中操作类似 区间修改,区间查询
    众所周知树状数组的一大经典功能是 区间修改,单点查询

    一维区间修改,单点查询

    我们设出一个差分数组 (d[]),设原数组为 (a[]),则 (d[i]=a[i]-a[i-1])
    (a[n]=sumlimits_{i=1}^n d[i])
    那么区间修改 “ ([l,r]) 加上 (c) ” ,对于差分数组,其实只需 (d[l]+=c) ,和 (d[r+1]-=c),也就是单点修改
    “单点查询” 查的是 (a[i]) ,也就是 (d[]) 的前缀和,用树状数组维护 (d[i]) 的某些区间的和即可。

    一维区间修改,区间查询

    区间查询 (sumlimits_{i=l}^r a[i]) ,需要的就不只是 (d[i]) 的前缀和了。
    先把查询分成 $sumlimits_{i=1}^r a[i] -sumlimits_{i=1}^{l-1} a[i] $
    然后把式子拆开:

    [egin{equation*} egin{aligned} sumlimits_{i=1}^n a[i] &= sumlimits_{i=1}^n sumlimits_{j=1}^i d[j] \ &= sumlimits_{i=1}^n d[i] imes (n-i+1) \ &= (n+1)sumlimits_{i=1}^n d[i] - sumlimits_{i=1}^n d[i] imes i end{aligned} end{equation*} ]

    于是我们需要用两个树状数组分别维护 (d[i])(d[i] imes i) 的某些区间的和。

    二维矩形修改,单点查询

    同样还是差分,差分数组 (d[i][j]) 满足 (a[n][m] =sumlimits_{i=1}^n sumlimits_{j=1}^m d[i][j])
    那么对于一个左下角 ((a,b)) 右上角 ((c,d)) 的矩形加上 (z) ,其实就是 (d[a][b]+=z)(d[c+1][d+1]+=z)(d[a][d+1]-=z)(d[c+1][b]-=z)
    可以理解成 (d[i][j]) 的值对所有它右上角的矩形的 (a[][]) 有影响。

    二维矩形修改,矩形查询

    把每个矩阵查询拆开,(sumlimits_{i=a}^c sumlimits_{j=b}^d a[i][j]=sumlimits_{i=1}^c sumlimits_{j=1}^d a[i][j]-sumlimits_{i=1}^c sumlimits_{j=1}^{b-1} a[i][j]-sumlimits_{i=1}^{a-1} sumlimits_{j=1}^d a[i][j] + sumlimits_{i=1}^{a-1} sumlimits_{j=1}^{b-1} a[i][j])
    然后再拆——

    [egin{equation*} egin{aligned} sumlimits_{i=1}^n sumlimits_{j=1}^m a[i][j] &= sumlimits_{i=1}^n sumlimits_{j=1}^m sumlimits_{k=1}^i sumlimits_{l=1}^j d[k][l] \ &= sumlimits_{i=1}^n sumlimits_{j=1}^m d[i][j] imes (n-i+1) imes (m-j+1) \ &= (n+1)(m+1)sumlimits_{i=1}^n sumlimits_{j=1}^m d[i][j] -(m+1) sumlimits_{i=1}^n sumlimits_{j=1}^m d[i][j] imes i -(n+1) sumlimits_{i=1}^n sumlimits_{j=1}^m d[i][j] imes j + sumlimits_{i=1}^n sumlimits_{j=1}^m d[i][j] imes i imes j end{aligned} end{equation*} ]

    维护4个树状数组即可。


    代码

    代码很是简短。

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    int read(){
    	int x=0,f=1;
    	char ch=getchar();
    	while(!isdigit(ch) && ch!='-') ch=getchar();
    	if(ch=='-') f=-1,ch=getchar();
    	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    	return f*x;
    }
    
    const int N = 2050;
    
    int n,m;
    int c[N][N],ci[N][N],cj[N][N],cij[N][N];
    void add(int x,int y,int d){
    	x=x?x:1; y=y?y:1;
    	for(int i=x;i<=n;i+=i&-i)
    		for(int j=y;j<=m;j+=j&-j){
    			c[i][j]+=d;
    			ci[i][j]+=x*d;
    			cj[i][j]+=y*d;
    			cij[i][j]+=x*y*d;
    		}
    }
    int sum(int x,int y){
    	int ret=0;
    	for(int i=x;i>0;i-=i&-i)
    		for(int j=y;j>0;j-=j&-j)
    			ret+=(x+1)*(y+1)*c[i][j]-(y+1)*ci[i][j]-(x+1)*cj[i][j]+cij[i][j];
    	return ret;
    }
    
    int main()
    {
    	char ch[2];
    	scanf("%s",ch);
    	n=read(); m=read();
    	
    	int a,b,c,d,delta;
    	while(scanf("%s",ch)!=EOF){
    		a=read(); b=read(); c=read(); d=read();
    		if(ch[0]=='L'){
    			delta=read();
    			add(a,b,delta); add(c+1,d+1,delta);
    			add(a,d+1,-delta); add(c+1,b,-delta);
    		}
    		else printf("%d
    ",sum(c,d)+sum(a-1,b-1)-sum(a-1,d)-sum(c,b-1));
    	}
    	
    	return 0;
    }
    
    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    Effective Java 19 Use interfaces only to define types
    Effective Java 18 Prefer interfaces to abstract classes
    Effective Java 17 Design and document for inheritance or else prohibit it
    Effective Java 16 Favor composition over inheritance
    Effective Java 15 Minimize mutability
    Effective Java 14 In public classes, use accessor methods, not public fields
    Effective Java 13 Minimize the accessibility of classes and members
    Effective Java 12 Consider implementing Comparable
    sencha touch SortableList 的使用
    sencha touch dataview 中添加 button 等复杂布局并添加监听事件
  • 原文地址:https://www.cnblogs.com/lindalee/p/11503503.html
Copyright © 2011-2022 走看看