水饺基情
1003 水饺基情
Time Limit : 3000/1000ms (Java/Other) Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 43 Accepted Submission(s) : 4
Font: Times New Roman | Verdana | Georgia
Font Size: ← →
Problem Description
在看完植物大战僵尸后,雄哥强烈要求zzy在203留宿,地点就在雄哥的睡袋上。是不是很基情??长夜漫漫无心睡眠,雄哥拿出了积攒已久的一盒水饺,(因为饿了= =)。半夜三更,四下无人,zzy和雄哥决定把水饺吃掉,但是!他们玩了一个十分基情的游戏。雄哥拿来了一张T*T的棋盘,决定把水饺铺到棋盘格子上。由于有两种味道的水饺,韭菜味的和白菜味的。韭菜味的用A表示,白菜味的用B表示。 将两种水饺以梅花分布形式铺满棋盘,例如:5*5的棋盘铺好后如下:
韭菜味 白菜味 韭菜味 白菜味 韭菜味
白菜味 韭菜味 白菜味 韭菜味 白菜味
韭菜味 白菜味 韭菜味 白菜味 韭菜味
白菜味 韭菜味 白菜味 韭菜味 白菜味
韭菜味 白菜味 韭菜味 白菜味 韭菜味
好了他们开始玩游戏了。约定两种操作
R a b c d:询问(a,b)->(c,d)范围内有多少个韭菜味和白菜味的水饺。
X a b: 代表将(a,b)点的水饺更新为X,X只有两种取值A或B。
好了,现在他们想知道多次更新后的棋盘在(a,b)->(c,d)的范围内有多少韭菜味的水饺和白菜味的水饺。
Input
输入包含多次操作,其中第一行是一个整数 T(1 ≤ T ≤ 1,000, 000),表示操作的次数,也是棋盘的宽度。
接下来是T行,分别对应Ti操作,格式有2种:
1.A或B开头,后跟2个整数x和y(1 ≤ x,y ≤ 1024),表示对(x,y)处的水饺进行更新
2.R 后面跟4个整数x1,y1,x2和y2(1 ≤ x1≤x2 ≤ 1024, 1 ≤ y1≤y2
≤ 1024), 询问(x1,y1)->( x2,y2)范围内有韭菜味和白菜味的水饺的个数。
Output
针对每个询问操作,输出一行,有两个整数 回答(x1,y1)->( x2,y2)范围内有多少韭菜味和白菜味的水饺。
Sample Input
8
R 1 1 5 5
A 5 5
R 1 1 5 5
R 1 1 4 5
A 1 4
A 2 4
A 3 4
R 1 1 5 5
Sample Output
13 12
13 12
10 10
15 10
比赛的时候目测是经典的二维线段树,然后开始纠结二维线段树怎么写,纠结了超过半小时后断然放弃(菜鸟的悲剧啊…)
赛后和大家讨论之后,发现其实只需稍稍转化下就可以用二维树状数组过掉的,可是那个时候我只会写一维的树状数组,压根儿不知二维所谓何物…
第二天上午起床之后果断开始学习二维树状数组,发现其实也不外如是。
如果用“点”这个几何概念来形容一维的话,那么二维树状数组更接近“面”的概念。参见下图做一些简单的说明:

现在我们想要知道(x1,y1)到(x2,y2)的这个子矩形的面积(或者认为他的每个点具有一定权值,这时可认为是求落在这个子矩形内的所有的点的权值和)。借助二维树状数组来解决是再方便不过的了。
因为二维树状数组的getsum(x,y)是(x,y)点到原点的“面积和”,那么要求上图中子矩形的面积,其实就是(x2,y2)到原点的面积减去(x2,y1)、(x1,y2)分别到原点的面积,再加上(x1,y1)到原点的面积(因为这一部分被减了2次)。相信大家对这个处理肯定不陌生吧? 这不是我们初中数学的几何体嘛!
好了,有了这些知识,那么应用二维树状数组就轻松啦!
针对这个题目,如果我们将韭菜味饺子(A)赋值为1,将白菜味饺子(B)赋值为0,那么对子矩形面积求和之后,得到的返回值即为韭菜味饺子(A)的个数,白菜味饺子(B)就是子矩形的“面积”减去韭菜味饺子(A)的数值嘛! 是不是还是蛮巧妙的呢?
下面给出二维树状数组的模板:
#define MAXN 1026 #define lowbit(x) x&(-x) int T, c[MAXN][MAXN]; void add(int x, int y, int val) { int i, j; for (i = x; i <= MAXN; i += lowbit(i)) for (j = y; j <= MAXN; j+= lowbit(j)) c[i][j] += val; } int getsum(int x, int y) { int sum = 0, i, j; for (i = x; i > 0; i -= lowbit(i)) for (j = y; j > 0; j -= lowbit(j)) sum += c[i][j]; return sum; }
不过针对这题,在初始化树状数组以及更新的时候还是做了一些简单的处理的:
int i, j, t, a, b, cc, d;
long long cnta, cntb; //求和的时候可能会因为数据太大溢出,这是保险的措施
char str[3];
while(~scanf("%d",&t)){
T= t;
memset(c,0,sizeof(c)); //同一维一样,需要初始化时清零
memset(matrix,0,sizeof(matrix));
if(T>1024) T=1024;
for(i=1; i<=T; ++i){
for(j=1; j<=T; ++j){
if((i%2 && j%2) || (i%2==0 && j%2==0)) {
update(i,j,1);
matrix[i][j] = 1; //建立了一个matrix的数组来跟踪整个棋盘饺子数目的变化
}
}
}
for(i=0; i<t; ++i){
scanf("%s",str);
if(str[0]=='R'){
scanf("%d %d %d %d",&a,&b,&cc,&d);
cnta = getsum(cc,d);
if(b>1) cnta-=getsum(cc,b-1);
if(a>1) cnta-=getsum(a-1,d);
if(a>1 && b>1) cnta+=getsum(a-1,b-1);
cntb = (cc-a+1)*(d-b+1)-cnta;
printf("%I64d %I64d\n",cnta,cntb);
}
else{
scanf("%d %d",&a,&b);
if(str[0]=='A' && matrix[a][b]==0){
update(a,b,1);
matrix[a][b] = 1; //这里的matrix数值一定要跟着改变
}
else if(str[0]=='B' && matrix[a][b]==1){
update(a,b,-1);
matrix[a][b] = 0;
}
}
}
}
其实是很简单的一个题目,可惜自己的算法知识太少,当时根本不会二维的,就没能做出来……
浙公网安备 33010602011771号