「IOI2001」Mobile Phones
IOI史诗级大水题!!!
题目大意:
对于每次查询,q==1
时在[X,Y]插入A,q==2
时查询区间{[L,B],[R,T]}
的数值和。
所以什么题目背景啊,标志0
和3
的意义啊、还有
L
≤
X
≤
R
L\le X\le R
L≤X≤R,
B
≤
Y
≤
T
B\le Y\le T
B≤Y≤T之类都是用来迷惑人的。
题目思路
对于q==1
,就是一个二维树状数组基本的插入操作,重点在q==2
时区间的数值和怎么求。
先想一想一维的树状数组:对于一个区间[L,R]
,要求它的数值和,我们可以用区间[1,R]
的数值和去减区间[l,L-1]
的数值和。
int query(int x)//求出1~i的数值和
{
int ans = 0;
for(int i = x; i; i -= lowbit(i))
{
ans += c[i];
}
return ans;
}
int main(){
/*......*/
int l, r, ans;
cin >> l >> r;
ans = query(r) - query(l-1);//用[1,r](前r个数的和)减去[1,l-1](前l-1个数的和)
}
类似地,二维的区间数值和也可以用相同的通过加减运算来得到吗?答案是肯定的,我们可以求出{[1,1],[x,y]}
的值,再通过矩形的加减运算求出所需区域。如下图:
矩形{[L,B],[R,T]}
(粉色)为题目所求的的区域,不难看出,粉色矩形 = 整个彩色矩形 - 蓝色区域(蓝线框起来的地方) - 黄色区域(黄线框起来的地方 + 绿色矩形。即:{[L,B],[R,T]} = {[1,1],[R,T]} - {[1,1],[L - 1,T]} - {[1,1],[R,B - 1]} + {[1,1],[L - 1,B - 1]}
。可以发现,等号后的矩形的值都是[1,1],[x,y]
的格式。因为题目给的描述是从[0,0]
开始的矩形,所以我们只需要对于每个L,B,R,T
加1,再把L,B
减一。至于为什么要减一呢?我们不妨把矩形压到一维数组试试:
int a[11] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, b[11];
int r = 9, l = 5;//求5~9=35
for(int i = 1; i <= 10; i++)
{
b[i] = b[i-1] + a[i];
}
/*b[r] - b[l] = b[9] - b[5] = 45 - 15 = 30;错误*/
b[r] - b[l-1] = b[9] - b[4] = 45 - 10 = 35;//因为[L,R]是闭区间,所以L要减一
同理,二维的数组也一样如此对于较小的矩形,它的x
和y
下标都应该减一。
那么答案也就显而易见了,直接用模板:
int query(int x, int y)//求出[1,1]~[x,y]的数值和
{
int ans = 0;
for (int i = x; i; i -= lowbit(i))
for (int j = y; j; j -= lowbit(j))
ans += a[i][j];
return ans;
}
int main(){
/*......*/
int ans, l, b, r, t;
cin >> l >> b >> r >> t;
l++, b++, r++, t++;
ans=query(r, t) - query(l - 1,) - (r, b - 1) + (l - 1, b - 1);//用{[1,1],[r,t]}(大矩形)减去{[1,1],[l - 1,t]},{[1,1],[r,b - 1]}(两个小矩形)再加上{[1,1],[l - 1,b - 1]}(重复运算的矩形)
}
CODE
#include <bits/stdc++.h>
using namespace std;
int a[2005][2005], n, q;
int lowbit(int x)
{
return x & (-x);
}
void update(int x, int y, int d)
{
for (int i = x; i <= n; i += lowbit(i))
for (int j = y; j <= n; j += lowbit(j))
a[i][j] += d;
}
int query(int x, int y)
{
int ans = 0;
for (int i = x; i; i -= lowbit(i))
for (int j = y; j; j -= lowbit(j))
ans += a[i][j];
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
while (cin >> q, q != 3)
{
int x, y, k, z;
if (q == 0)
cin >> n;
if (q == 1)
{
cin >> x >> y >> k;
update(x + 1, y + 1, k);
}
if (q == 2)
{
cin >> x >> y >> k >> z;
x++, y++, k++, z++;
cout << query(k, z) - query(x - 1, z) - query(k, y - 1) + query(x - 1, y - 1) << endl;
}
}
return 0;
}