码迷,mamicode.com
首页 > 编程语言 > 详细

[luogu] P4514 上帝造题的七分钟 (树状数组,二维差分)

时间:2018-10-02 20:27:12      阅读:211      评论:0      收藏:0      [点我收藏+]

标签:inline   space   接下来   32位   xlk   void   输入输出格式   read   区域   

P4514 上帝造题的七分钟

题目背景

裸体就意味着身体。

题目描述

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

输入输出格式

输入格式:

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

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

请注意,k为小写。

输出格式:

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

输入输出样例

输入样例#1: 复制

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

输出样例#1: 复制

12

说明

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




题解

想写二维线段树。
然而讨论里说卡掉了空间了。
那就硬着头皮写二维树状数组吧。
二维树状数组教做人系列。

对于一维的树状数组,我们知道区间加和区间求和是需要差分并维护两个树状数组的。(不会的可以去用树状数组写一下x谷的线段树模板1)
这个对于二维树状数组同样适用。
那么我们假设\(sum[i][j]\)为差分数组。
\(sum[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]\)
为什么要这么假设呢?
因为这样我们可以发现
对于点\((x,y)\)的值就是
\(\sum_{i=1}^x\sum_{j=1}^y\sum_{h=1}^i\sum_{k=1}^j sum[h][k]\)
根据每一次的求值,我们发现以\((x,y)\)为结尾的矩阵,每个差分数组出现的次数为
\(\sum_{i=1}^x\sum_{j=1}^y sum[i][j]*(x-i+1)*(y-j+1)\)
是不是会发现开始变得和一维树状数组求法一样了。

接下来让我们把这个公式拆开食用。
\(\sum_{i=1}^x\sum_{j=1}^y sum[i][j]*(xy+x+y+1)-sum[i][j]*i(y+1)-sum[i][j]*j(x+1)+sum[i][j]*i*j\)
发现只要维护\(sum[i][j],sum[i][j]*i,sum[i][j]*j,sum[i][j]*i*j\)四个树状数组了。

好现在考虑加值。给\((2,2),(3,3)\)的矩阵加上值。
在差分数组里面就是这样的

0 0 0 0 0 
0 + 0 - 0
0 0 0 0 0
0 - 0 + 0

就等于正常数组里面的

0 0 0 0 0
0 + + 0 0
0 + + 0 0
0 0 0 0 0

即在给\((x_1,y_1),(x_2,y_2)\)加值时。我们需要给\((x_1,y_1),(x_2+1,y_2+1)\)加,\((x_1,y_2+1),(x_2+1,y_1)\)减。
因为统计数组时,点\((i,j)\)的值为以\((i,j)\)为右下角,\((1,1)\)为左上角的差分矩阵的和。

题解

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
int read(){
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}

struct node{
    int t[2050][2050];
    void add(int x,int y,int v){
        for(int i=x;i<=n;i+=(i&(-i)))
            for(int j=y;j<=m;j+=(j&(-j)))
            t[i][j]+=v;
    }
    int query(int x,int y){
        int sum=0;
        for(int i=x;i;i-=(i&(-i)))
            for(int j=y;j;j-=(j&(-j)))
            sum+=t[i][j];
        return sum;
    }
}Q,Qi,Qj,Qij;

void add(int x,int y,int v){
    Q.add(x,y,v);Qi.add(x,y,v*x);
    Qj.add(x,y,v*y);Qij.add(x,y,v*x*y);
}

int query(int x,int y){
    return Q.query(x,y)*(x*y+x+y+1)-Qi.query(x,y)*(y+1)-Qj.query(x,y)*(x+1)+Qij.query(x,y);
}

int main(){
    n=read();m=read();
    char opt[10];
    while(scanf("%s",opt)==1){
        int xa=read(),ya=read(),xb=read(),yb=read();
        if(opt[0]=='L'){
            int v=read();
            add(xa,ya,v);add(xb+1,ya,-v);
            add(xb+1,yb+1,v);add(xa,yb+1,-v);
        }
        else {
            printf("%d\n",query(xb,yb)-query(xb,ya-1)-query(xa-1,yb)+query(xa-1,ya-1));
        }
    }
    return 0;
}

[luogu] P4514 上帝造题的七分钟 (树状数组,二维差分)

标签:inline   space   接下来   32位   xlk   void   输入输出格式   read   区域   

原文地址:https://www.cnblogs.com/hhh1109/p/9737494.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!