【bzoj 1176】[Balkan2007] Mokia(cdq分治+树状数组)
1176: [Balkan2007]Mokia
Time Limit: 30 Sec Memory Limit: 162 MBSubmit: 1852 Solved: 820
[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
【题解】【cdq分治】
【cdq分治,把操作一起二分。因为是二维求和,首先想到的应该是二维线段树或二维树状数组,然而,这范围、、、TLE+MLE!】
【那么,考虑一个经典的解决方法,一维排序、一维树状数组维护前缀和。】
【因为询问的是一个子矩阵和,那么把整个矩阵拆分为4块,两块需要减去、两块需要加上。(刚开始没拆开,用加正负数来处理,WA啊WA!)so把每个询问拆成两个,x为一个,x1为一个,都是求前缀和,最后用后一个减前一个得到答案】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct slove{
int x,y,y1,k;
int num,opt,mark,sum;
int t;
}a[2000010];
int tree[2000010],ans[2000010],pd[2000010];
int s,w,cnt,tot;
int tmp(slove a,slove b)
{
return a.x<b.x||a.x==b.x&&a.opt<b.opt;
}
int cmp(slove a,slove b)
{
return a.num<b.num||a.num==b.num&&a.mark<b.mark;
}
inline int lowbit(int x)
{
return x&(-x);
}
inline void add(int x,int val)
{
while(x<=w)
{
tree[x]+=val;
x+=lowbit(x);
}
}
inline int ask(int x)
{
int ss=0;
while(x)
{
ss+=tree[x];
x-=lowbit(x);
}
return ss;
}
void cdq(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1;
cdq(l,mid); cdq(mid+1,r);
sort(a+l,a+mid+1,tmp);
sort(a+mid+1,a+r+1,tmp);
int j=l;
for(int i=mid+1;i<=r;++i)
if(a[i].opt==2)
{
while(j<=mid&&a[j].x<=a[i].x)
{
if(a[j].opt==1) add(a[j].y,a[j].k);
++j;
}
a[i].sum+=ask(a[i].y1)-ask(a[i].y);
}
for(int i=l;i<j;++i)
if(a[i].opt==1) add(a[i].y,-a[i].k);
}
int main()
{
int i;
scanf("%d%d",&s,&w);
while((scanf("%d",&a[++cnt].opt)==1)&&a[cnt].opt!=3)
{
tot++;
if(a[cnt].opt==1) scanf("%d%d%d",&a[cnt].x,&a[cnt].y,&a[cnt].k),a[cnt].num=tot;
else
{
int x,y,x1,y1; pd[tot]=1;
scanf("%d%d%d%d",&x,&y,&x1,&y1);
a[cnt].x=x-1; a[cnt].y=y-1; a[cnt].y1=y1; a[cnt].num=tot; a[cnt].mark=-1;
cnt++; a[cnt].opt=2; a[cnt].x=x1; a[cnt].y=y-1; a[cnt].y1=y1; a[cnt].num=tot; a[cnt].mark=1;
}
}
cnt--;
cdq(1,cnt);
for(i=1;i<=cnt;++i)
ans[a[i].num]+=a[i].sum*a[i].mark;
for(i=1;i<=tot;++i)
if(pd[i]) printf("%d\n",ans[i]);
return 0;
}
既然无能更改,又何必枉自寻烦忧