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
Sample Output
3
5
HINT
保证答案不会超过int范围
题解:
经典的CDQ分治问题,属于较早期的题目。我们利用前缀和记录每个点到左上角的元素和,将询问通过容斥原理拆成4个,这时每个询问或修改都只涉及两维坐标,加上他们本身的时间顺序,构成了标准的三维偏序问题。
将询问与修改按X轴进行排序,对Y轴建立树状数组,那么如果X从小到大排,就相当于求Y轴上的前缀和。分治左右,然后计算修改在mid左边,查询在mid右边的情况。
以上也是大多数CDQ分治的套路。
代码如下:
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long
#define inf 0x7f7f7f7f
#define lb(x) (x&(-x))
using namespace std;
ll read()
{
ll x=0,f=1;
char c=getchar();
while(c<'0' || c>'9') {if(c=='-') f=-1;c=getchar();}
while(c<='9' && c>='0') {x=x*10+c-'0';c=getchar();}
return x*f;
}
int s,w,m,ans[10005],t[2000005],Count;
struct que
{
int x,y,val,pos,id,opt;
bool operator < (const que& b) const
{
if(x==b.x && y==b.y) return opt<b.opt;
if(x==b.x) return y<b.y;
return x<b.x;
}
}q[200005],tmp[200005];
void addquery()
{
int x1=read(),y1=read(),x2=read(),y2=read();
int pos=++Count;
q[++m].pos=pos;q[m].id=m;q[m].x=x1-1;q[m].y=y1-1;q[m].val=1;q[m].opt=2;
q[++m].pos=pos;q[m].id=m;q[m].x=x2;q[m].y=y2;q[m].val=1;q[m].opt=2;
q[++m].pos=pos;q[m].id=m;q[m].x=x1-1;q[m].y=y2;q[m].val=-1;q[m].opt=2;
q[++m].pos=pos;q[m].id=m;q[m].x=x2;q[m].y=y1-1;q[m].val=-1;q[m].opt=2;
}
void add(int x,int val)
{
while(x<=w)
{
t[x]+=val;
x+=lb(x);
}
}
int query(int x)
{
int ret=0;
while(x)
{
ret+=t[x];
x-=lb(x);
}
return ret;
}
void cdq(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1,l1=l,l2=mid+1;
for(int i=l;i<=r;i++)
{
if(q[i].id<=mid && q[i].opt==1) add(q[i].y,q[i].val);
if(q[i].id>mid && q[i].opt==2) ans[q[i].pos]+=q[i].val*query(q[i].y);
}
for(int i=l;i<=r;i++) if(q[i].id<=mid && q[i].opt==1) add(q[i].y,-q[i].val);
for(int i=l;i<=r;i++)
{
if(q[i].id<=mid) tmp[l1++]=q[i];
else tmp[l2++]=q[i];
}
for(int i=l;i<=r;i++) q[i]=tmp[i];
cdq(l,mid);cdq(mid+1,r);
}
int main()
{
s=read();w=read();
while(1)
{
int opt=read();
if(opt==1)
{
q[++m].x=read(),q[m].y=read(),q[m].val=read(),q[m].opt=1;
q[m].id=m;
}
else if(opt==2) addquery();
else break;
}
sort(q+1,q+m+1);
cdq(1,m);
for(int i=1;i<=Count;i++) printf("%d\n",ans[i]);
return 0;
}