HLJU 1188 Matrix (二维树状数组)

Matrix

Time Limit: 4 Sec   Memory Limit: 128 MB

Description

给定一个1000*1000的二维矩阵,初始矩阵中每个数都为1,然后为矩阵有4种操作.

S x1 y1 x2 y2:计算(x1,y1)、(x2,y2)围成的矩阵内所有元素的和。

A x y v:将(x,y)增加v

D x y v:将(x,y)减少v

M x1 y1 x2 y2 v:将(x1,y1)元素中的v转移到(x2,y2)中去。

所有操作数都为正数。

若某一操作将矩阵中元素降到1以下,一律按1处理。

x,y从0开始编号。

Input

 第一行一个数t,代表样例个数:

每组样例第一行一个数m,代表m次操作,m<100000

接下来m行代表m次操作

Output

 每组样例输出一个

Case i:

i代表第i个样例

 对于每一个操作S,输出一行,代表计算结果。

所有结果均不会超过int范围

Sample Input

1
4
A 1 1 1
M 1 1 2 2 1
D 2 2 1
S 1 1 2 2

Sample Output

Case 1:
4



解题思路:很明显的就是一个很典型的二维树状数组问题,树状数组部分完全就是模板,只是要在具体解决实际问题的时候,有点技巧。最开始见到这题的时候,我立即想到了以前切的楼教主出的Matrix,感觉很相似,然后就直接搞了,后来发现开始,要初始化树状数组的c数组,由于矩阵的初值均为1,就用了两层循环,一个一个调用update(),结果竟然超时了。。。然后又在其他地方优化了,还是超时。。。就想了个办法,直接把c数组全部初始化为0,这样就不用一次次的去调用update()了(因为c要存的是他前面的和,现在全为0,和当然也为0了),然后在求区域和的时候,直接加上那个区间里的节点数(因为本来的矩阵中应该全部初始化为1的,但是我开始全初始化为0了,就把每个节点都少了1,节点数也就是区间的面积)即可。还有就是要求保证原矩阵元素在操作之后不能小于1,具体可以先算出当前位置的元素值, 在操作之前把操作的数e修改,然后直接执行操作即可。这里还学到了一个求当前位置元素的方法,直接用区域求和函数,对当前点求和,即map[a][b] = getsum( a, b, a, b ). 详见代码


楼教主的Matrix 见:http://blog.csdn.net/u013446688/article/details/38194977



AC代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1002;

#define lowbit(x)	x & (-x)       //lowbit函数            

int c[maxn][maxn];                     //树状数组的c数组

inline int update(int x, int y, int d){      //update函数 
	int i, j;
	for(i=x; i<=1000; i+=lowbit(i))
		for(j=y; j<=1000; j+=lowbit(j))
			c[i][j] += d;
}


inline int sum(int x, int y){         //sum函数 
	int ans = 0;
	int i, j;
	for(i=x; i>0; i-=lowbit(i))
		for(j=y; j>0; j-=lowbit(j))
			ans += c[i][j];
	return ans;
}

int getsum(int x1, int y1, int x2, int y2){       //区域求和
	return  sum(x2, y2) - sum(x1-1, y2) - sum(x2, y1-1) + sum(x1-1, y1-1);
}

int main(){
//	freopen("in.txt","r",stdin);
	int T, m ,a, b, cc, d, e;
	string p;
	scanf("%d", &T);
	for(int t=1; t<=T; t++){
		scanf("%d", &m);
		printf("Case %d:\n", t);
		memset(c, 0, sizeof(c));
		while(m--){
			cin>>p;
			if(p[0] == 'A'){
				scanf("%d%d%d", &a, &b, &e);
				a++; b++;
				update(a, b, e);
			}
			else if(p[0] == 'D'){
				scanf("%d%d%d", &a, &b, &e);
				a++; b++;
				int k = getsum(a, b, a, b);      //求当前位置的元素的值
				if(k - e < 0)  e = k;            //修改e, 保证操作之后元素不小于1
				update(a, b, -e);
			}
			else if(p[0] == 'M'){
				scanf("%d%d%d%d%d", &a, &b, &cc, &d, &e);
				a++; b++; cc++; d++;
				int k = getsum(a, b, a, b);      //同上
				if(k - e < 0)   e = k;
				update(a, b, -e);

				update(cc, d, e);
			}
			else{
				scanf("%d%d%d%d", &a, &b, &cc, &d);
				a++; b++; cc++; d++;
				if(cc < a)  swap(a, cc);
				if(d < b)  swap(b, d);
				int ans = getsum(a, b, cc, d) + (cc-a+1)*(d-b+1);    //求矩阵区域和
				printf("%d\n",ans);
			}
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个使用Verilog语言驱动LCD1602显示"HLJU, I LOVE U!"的示例代码: ``` module lcd1602( input clk, input rst_n, input [7:0] data, input rs, input rw, output enable, output [15:0] lcd_data ); // 内部信号定义 reg [7:0] lcd_buffer [0:31]; reg [5:0] addr; reg write_enable; // 初始化内部信号 initial begin addr = 0; write_enable = 0; lcd_buffer[0] = 8'h48; // H lcd_buffer[1] = 8'h4C; // L lcd_buffer[2] = 8'h4A; // J lcd_buffer[3] = 8'h55; // U lcd_buffer[4] = 8'h2C; // , lcd_buffer[5] = 8'h20; // 空格 lcd_buffer[6] = 8'h49; // I lcd_buffer[7] = 8'h20; // 空格 lcd_buffer[8] = 8'h4C; // L lcd_buffer[9] = 8'h4F; // O lcd_buffer[10] = 8'h56; // V lcd_buffer[11] = 8'h45; // E lcd_buffer[12] = 8'h20; // 空格 lcd_buffer[13] = 8'h55; // U lcd_buffer[14] = 8'h21; // ! end // 计时器模块 reg [23:0] counter; always @(posedge clk) begin if (!rst_n) begin counter <= 0; end else begin counter <= counter + 1; end end // 使能信号生成模块 reg [1:0] enable_cnt; always @(posedge clk) begin if (!rst_n) begin enable_cnt <= 0; enable <= 0; end else begin case (enable_cnt) 0: begin enable <= 1; lcd_data <= {rs, rw, lcd_buffer[addr]}; end 1: begin enable <= 0; lcd_data <= 16'h0000; end 15: begin enable_cnt <= 0; addr <= (addr == 31) ? 0 : (addr + 1); write_enable <= 1; end default: begin enable <= 0; lcd_data <= 16'h0000; end endcase enable_cnt <= enable_cnt + 1; end end // 数据写入模块 always @(posedge clk) begin if (!rst_n) begin write_enable <= 0; end else begin if (write_enable) begin lcd_buffer[addr] <= data; end write_enable <= 0; end end endmodule ``` 上面的代码中,使用一个计时器模块来产生一个时钟信号,计时器的计数频率是时钟信号的频率的16倍。然后使用一个使能信号生成模块来生成LCD1602的使能信号和数据。在这个模块中,使用一个计数器来计数,根据计数器的值来产生使能信号和数据。最后,使用一个数据写入模块来将数据写入到LCD1602的缓冲区中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值