1176: [Balkan2007]Mokia
Time Limit: 30 Sec Memory Limit: 162 MB[ Submit][ Status][ Discuss]
Description
维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.
Input
第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小
接下来每行为一下三种输入之一(不包含引号):
"1 x y a"
"2 x1 y1 x2 y2"
"3"
输入1:你需要把(x,y)(第x行第y列)的格子权值增加a
输入2:你需要求出以左下角为(x1,y1),右上角为(x2,y2)的矩阵内所有格子的权值和,并输出
输入3:表示输入结束
Output
对于每个输入2,输出一行,即输入2的答案
Sample Input
0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
Sample Output
3
5
5
HINT
保证答案不会超过int范围
Source
这道题的s是没有用的,不信看讨论
dalao说能用kd-tree搞,我不知道这kd-tree什么东西,但是为什么没有人写啊
对于cdq分治这个没有规范定义的东西也是不太懂
首先查询一个块可以四个块加加减减得到,应该都会吧...
那就先把一个查询分为四个部分
cdq是要离线操作的
按照x排序,以y为第二关键字
每次cdq分治时
按照时间二分区间
记得要清空树状数组
#include<algorithm>
#include<cstdio>
const int N = 2000000 + 5;
const int M = 10005;
int s,w,n=0,ans[M],c[N];
struct data{
int x,y,type,pos,v,id;
}a[N/2],b[N/2];
bool cmp( data a, data b ){
if( a.x == b.x && a.y == b.y ) return a.type < b.type;
if( a.x == b.x ) return a.y < b.y;
return a.x < b.x;
}
void modify( int k, int v ){
for( int i = k; i <= w; i += i&-i ) c[i] += v;
}
int query( int k ){
int res = 0;
for( int i = k; i; i -= i&-i ) res += c[i];
return res;
}
void cdq( int l, int r ){
if( l == r ) return ;
int mid = (l+r)>>1, t1 = l-1, t2 = mid;
for( int i = l; i <= r; i++ ){
if( a[i].id <= mid && !a[i].type ) modify(a[i].y,a[i].v);
if( a[i].id > mid && a[i].type ) ans[a[i].pos] += a[i].v*query(a[i].y);
}
for( int i = l; i <= r; i++ ) if( a[i].id <= mid && !a[i].type ) modify(a[i].y,-a[i].v);
for( int i = l; i <= r; i++ ) if( a[i].id <= mid ) b[++t1] = a[i]; else b[++t2] = a[i];
for( int i = l; i <= r; i++ ) a[i] = b[i];
cdq(l,mid); cdq(mid+1,r);
}
int main(){
scanf("%d%d", &s, &w);
while(1){
int opt,x1,y1,x2,y2;
scanf("%d", &opt);
if( opt == 1 ){
n++; scanf("%d%d%d", &a[n].x, &a[n].y, &a[n].v); a[n].id = n;
}
if( opt == 2 ){
scanf("%d%d%d%d", &x1, &y1, &x2, &y2); ans[0]++;
n++; a[n].x = x2; a[n].y = y2; a[n].v = 1; a[n].type = 1; a[n].id = n; a[n].pos = ans[0];
n++; a[n].x = x1-1; a[n].y = y1-1; a[n].v = 1; a[n].type = 1; a[n].id = n; a[n].pos = ans[0];
n++; a[n].x = x2; a[n].y = y1-1; a[n].v = -1; a[n].type = 1; a[n].id = n; a[n].pos = ans[0];
n++; a[n].x = x1-1; a[n].y = y2; a[n].v = -1; a[n].type = 1; a[n].id = n; a[n].pos = ans[0];
}
if( opt == 3 ) break;
}
std::sort(a+1,a+n+1,cmp); cdq(1,n);
for( int i = 1; i <= ans[0]; i++ ) printf("%d\n", ans[i]);
return 0;
}
总结
以后写一道题要写总结了
分治时把'1'和'l'分别开,什么时候是1,什么时候是l
对于有些相似操作(比如modify和change),拷贝过来时要检查变量有没有改完