POJ 1195 Mobile phones

题目大意:

        现只有一个测例,测例中将给出若干命令,命令形式为"CMD STATEMENT",CMD为命令类型,STATEMENT为命令内容,最先给出"0 S",表示初始化一个S × S的矩阵,所有元素都为0,每个元素范围为[0, 32767](在任何时候都是出于这个范围),最后给出命令"3"表示程序结束,另外有两种操作,一种是"1 X Y A",表示将元素(X, Y)加上A,其中矩阵编号从0开始,因此0 ≤ X, Y ≤ S - 1,其中A的范围为[-32768, 32767],还有一种命令为"2 L B R T",表示要求输出子矩阵[L, R] × [B, T]中所有元素之和,输入数据保证整个矩阵所有元素之和不超过2 ^ 30。

题目链接

二维树状数组:

注释代码:

/*             
 * Problem ID : POJ 1195 Mobile phones 
 * Author     : Lirx.t.Una             
 * Language   : C        
 * Run Time   : 532 ms             
 * Run Memory : 4276 KB             
*/

#include <stdio.h>

//矩阵的最大宽度
#define	MAXS		1024

short	lowbit[MAXS + 1];//打表
int		c[MAXS + 1][MAXS + 1];//二维树状数组
int		s;//矩阵大小

void
update( int x, int y, int v ) {//元素(x, y)加上v

	int		yy;//临时变量

	while ( x <= s ) {//逐行进行跟新
		//每一行都是一个树状数组
		//同时纵向(x方向)也是一个树状数组,只不过
		  //x方向上每一个元素都是一个树状数组
	
		yy = y;//先对一行进行更新
		while ( yy <= s ) {
		
			c[x][yy] += v;
			yy		 += lowbit[yy];
		}

		x += lowbit[x];//到下一行
	}
}

int
query( int x, int y ) {//求左上角矩阵[1, x]×[1, y]所有元素之和
	
	int		sum;
	int		yy;

	sum = 0;//同理
	while ( x > 0 ) {
	
		yy = y;
		while ( yy > 0 ) {
		
			sum += c[x][yy];
			yy  -= lowbit[yy];
		}

		x -= lowbit[x];
	}

	return sum;
}

int
main() {

	int		cmd;
	int		i;//计数变量
	int		x, y, a;//命令2的变量
	int		l, r, b, t;//命令3的变量
	//(l, b)为左上角,(r, t)为右下角

	while ( scanf("%d", &cmd), cmd != 3 ) {
	
		switch (cmd) {

			case 0 ://开始命令,可以进行初始化

				scanf("%d", &s);
				for ( i = 1; i <= s; i++ )
					lowbit[i] = i & -i;

				break;

			case 1 ://更新命令

				scanf("%d%d%d", &x, &y, &a);
				update( x + 1, y + 1, a );

				break;

			case 2 ://求和命令

				scanf("%d%d%d%d", &l, &b, &r, &t);
					
				l++;
				b++;
				r++;
				t++;

				printf("%d\n",
						query( r, t ) 		  +//整个矩阵
						query( l - 1, b - 1 ) -//左上角小矩阵
						query( r, b - 1 ) 	  -//左下角子矩阵
						query( l - 1, t ));//右上角子矩阵

				break;

			default : break;
		}
	}

	return 0;
}
无注释代码:

#include <stdio.h>

#define	MAXS		1024

short	lowbit[MAXS + 1];
int		c[MAXS + 1][MAXS + 1];
int		s;

void
update( int x, int y, int v ) {

	int		yy;

	while ( x <= s ) {
	
		yy = y;
		while ( yy <= s ) {
		
			c[x][yy] += v;
			yy		 += lowbit[yy];
		}

		x += lowbit[x];
	}
}

int
query( int x, int y ) {

	int		sum;
	int		yy;

	sum = 0;
	while ( x > 0 ) {
	
		yy = y;
		while ( yy > 0 ) {
		
			sum += c[x][yy];
			yy  -= lowbit[yy];
		}

		x -= lowbit[x];
	}

	return sum;
}

int
main() {

	int		cmd;
	int		i;
	int		x, y, a;
	int		l, r, b, t;

	while ( scanf("%d", &cmd), cmd != 3 ) {
	
		switch (cmd) {

			case 0 :

				scanf("%d", &s);
				for ( i = 1; i <= s; i++ )
					lowbit[i] = i & -i;

				break;

			case 1 :

				scanf("%d%d%d", &x, &y, &a);
				update( x + 1, y + 1, a );

				break;

			case 2 :

				scanf("%d%d%d%d", &l, &b, &r, &t);
					
				l++;
				b++;
				r++;
				t++;

				printf("%d\n",
						query( r, t ) 		  +
						query( l - 1, b - 1 ) -
						query( r, b - 1 ) 	  -
						query( l - 1, t ));

				break;

			default : break;
		}
	}

	return 0;
}

二维线段树:

注释代码:

/*             
 * Problem ID : POJ 1195 Mobile phones 
 * Author     : Lirx.t.Una             
 * Language   : C        
 * Run Time   : 1094 ms             
 * Run Memory : 16584 KB             
*/

#include <stdio.h>

#define	MAXS		1024

#define	LFT(T)		( (T) << 1 )
#define	RHT(T)		( LFT(T) | 1 )

int		seg[MAXS << 1][MAXS << 1];//二维线段树,这里两倍大小也能AC
//在seg[x][y]中,x指示纵向线段树的结点,y指示横向线段树的结点
//如果tree[x]表示纵向的[x1, x2],tree[y]表示横向的[y1, y2]
//则seg[x][y]表示子矩阵[x1, x2]×[y1, y2]
int		s;//矩阵的大小

void
update_line( int tx, int ty, int y, int v, int lft, int rht ) {
	//tree_x和tree_y表示两个方向上个的线段树,x为纵向的,y为横向的
	//update_line表示先对横向进行更新,横向为y轴,纵向为x轴
	//tx不动,对ty表示的线段树进行更新
	//修改的横坐标为y,增量为v,横向线段树结点ty的区间为[lft, rht]
	//!!!这里只更新了一行,即tx这一行的线段树

	int		mid;

	seg[tx][ty] += v;//现更新当前结点ty的值
	if ( lft != rht ) {//横向分解

		mid = ( lft + rht ) >> 1;
		if ( y <= mid ) update_line( tx, LFT(ty), y, v, lft, mid );
		else update_line( tx, RHT(ty), y, v, mid + 1, rht );
	}
}

void
update( int tx, int x, int y, int v, int lft, int rht ) {
	//逐行更新,对x所在范围内的纵向线段树中的结点进行更新
	//更新点坐标为(x, y)
	//横向线段树当前结点为tx,其结点区间为[lft, rht]
	//注意!此时[lft, rht]表示纵向的范围了!

	int		mid;

	update_line( tx, 1, y, v, 0, s - 1 );//现更新当前行

	if ( lft != rht ) {//纵向分解,更新相应行即可
	
		mid = ( lft + rht ) >> 1;
		if ( x <= mid ) update( LFT(tx), x, y, v, lft, mid );
		else update( RHT(tx), x, y, v, mid + 1, rht );
	}
}

int
query_line( int tx, int ty, int ql, int qr, int lft, int rht ) {
	//横向查询,如果当前行所在的纵向线段树的结点为tx,并且其结点区间为[lft_x, rht_x]
	//则query_line返回的就是子矩阵[lft_x, rht_x]×[lft, rht]内元素之和(如果lft == ql && qr == rht的话)
	//因此[lft, rht]为当前横向线段树结点ty的结点区间
	//[ql, qr]则为行线段树查询区间

	int		mid;

	//横向查询区间命中
	if ( lft == ql && qr == rht ) return seg[tx][ty];

	mid = ( lft + rht ) >> 1;

	//未命中则横向分解
	if ( qr <= mid ) return query_line( tx, LFT(ty), ql, qr, lft, mid );
	else if ( ql > mid ) return query_line( tx, RHT(ty), ql, qr, mid + 1, rht );
	else return query_line( tx, LFT(ty), ql, mid, lft, mid ) +
				query_line( tx, RHT(ty), mid + 1, qr, mid + 1, rht );
}

int	
query( int tx, int l, int r, int b, int t, int lft, int rht ) {
	//纵向查询
	//[lft, rht]表示纵向线段树结点tx的结点区间
	//[b, t]为纵向查询区间,[l, r]为横向查询区间

	int		mid;

	//纵向区间命中,则继续查询横向区间
	if ( lft == b && t == rht ) return query_line( tx, 1, l, r, 0, s - 1 );

	mid = ( lft + rht ) >> 1;

	//纵向分解
	if ( t <= mid ) return query( LFT(tx), l, r, b, t, lft, mid );
	else if ( b > mid ) return query( RHT(tx), l, r, b, t, mid + 1, rht );
	else return query( LFT(tx), l, r, b, mid, lft, mid ) +
				query( RHT(tx), l, r, mid + 1, t, mid + 1, rht );
}

int
main() {

	int		cmd;
	int		x, y, a;
	int		l, r, b, t;

	while ( scanf("%d", &cmd), cmd != 3 ) {
	
		switch (cmd) {
		
			case 0 :
				
				scanf("%d", &s);
				break;

			//由于不是树状数组,因此线段树结点下标可以从0开始!!
			case 1 :

				scanf("%d%d%d", &x, &y, &a);
				update( 1, x, y, a, 0, s - 1 );
				break;

			case 2 :

				scanf("%d%d%d%d", &l, &b, &r, &t);
				printf("%d\n", query( 1, b, t, l, r, 0, s - 1 ));
				break;

			default : break;
		}
	}

	return 0;
}
无注释代码:

#include <stdio.h>

#define	MAXS		1024

#define	LFT(T)		( (T) << 1 )
#define	RHT(T)		( LFT(T) | 1 )

int		seg[MAXS << 1][MAXS << 1];
int		s;

void
update_line( int tx, int ty, int y, int v, int lft, int rht ) {

	int		mid;

	seg[tx][ty] += v;
	if ( lft != rht ) {

		mid = ( lft + rht ) >> 1;
		if ( y <= mid ) update_line( tx, LFT(ty), y, v, lft, mid );
		else update_line( tx, RHT(ty), y, v, mid + 1, rht );
	}
}

void
update( int tx, int x, int y, int v, int lft, int rht ) {

	int		mid;

	update_line( tx, 1, y, v, 0, s - 1 );

	if ( lft != rht ) {
	
		mid = ( lft + rht ) >> 1;
		if ( x <= mid ) update( LFT(tx), x, y, v, lft, mid );
		else update( RHT(tx), x, y, v, mid + 1, rht );
	}
}

int
query_line( int tx, int ty, int ql, int qr, int lft, int rht ) {

	int		mid;

	if ( lft == ql && qr == rht ) return seg[tx][ty];

	mid = ( lft + rht ) >> 1;

	if ( qr <= mid ) return query_line( tx, LFT(ty), ql, qr, lft, mid );
	else if ( ql > mid ) return query_line( tx, RHT(ty), ql, qr, mid + 1, rht );
	else return query_line( tx, LFT(ty), ql, mid, lft, mid ) +
				query_line( tx, RHT(ty), mid + 1, qr, mid + 1, rht );
}

int	
query( int tx, int l, int r, int b, int t, int lft, int rht ) {

	int		mid;

	if ( lft == b && t == rht ) return query_line( tx, 1, l, r, 0, s - 1 );

	mid = ( lft + rht ) >> 1;

	if ( t <= mid ) return query( LFT(tx), l, r, b, t, lft, mid );
	else if ( b > mid ) return query( RHT(tx), l, r, b, t, mid + 1, rht );
	else return query( LFT(tx), l, r, b, mid, lft, mid ) +
				query( RHT(tx), l, r, mid + 1, t, mid + 1, rht );
}

int
main() {

	int		cmd;
	int		x, y, a;
	int		l, r, b, t;

	while ( scanf("%d", &cmd), cmd != 3 ) {
	
		switch (cmd) {
		
			case 0 :
				
				scanf("%d", &s);
				break;

			case 1 :

				scanf("%d%d%d", &x, &y, &a);
				update( 1, x, y, a, 0, s - 1 );
				break;

			case 2 :

				scanf("%d%d%d%d", &l, &b, &r, &t);
				printf("%d\n", query( 1, b, t, l, r, 0, s - 1 ));
				break;

			default : break;
		}
	}

	return 0;
}
单词解释:

at times:adv, 有时,偶尔

switch on/off:vi, 打开/关闭

switch:n, 开关; vi, 转换

Tampere:坦佩雷(芬兰西南部一座城市)

base station:n, 基站

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值