题目大意:
现只有一个测例,测例中将给出若干命令,命令形式为"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, 基站