★★★☆
输入文件:locust.in 输出文件:locust.out 简单对比
时间限制:2 s 内存限制:128 MB
DESCRIPTION
C国国土辽阔,地大物博……但是最近却在闹蝗灾…..
我们可以把C国国土当成一个W×W的矩阵,你会收到一些诸如(X,Y,Z)的信息,代表(X,Y)这个点增多了
Z只蝗虫,而由于C国政府机关比较臃肿,为了批复消灭蝗虫的请求需要询问一大堆的问题……每个询问形如
(X1,Y1,X2,Y2),询问在(X1,Y1,X2,Y2)范围内有多少蝗虫(请注意询问不会改变区域内的蝗虫数),
你作为一个C国的程序员,需要编一个程序快速的回答所有的询问。
NOTICE
C国一开始没有蝗虫。
INPUT
输入文件的第一行包括一个整数W,代表C国国土的大小。第二行有一个整数N,表示事件数。接下来有N行表示N个事件,以(1 X Y Z)的形式或(2,X1,Y1,X2,Y2)的形式给出,分别代表蝗虫的增加和询问。
OUTPUT
对于每个询问输出一个整数表示需要的结果。
SAMPLE INPUT
locust.in
5
8
2 4 1 4 2
1 3 1 8
1 4 4 4
2 1 3 4 4
1 1 5 1
1 4 4 5
2 2 2 5 4
2 3 2 4 4
SAMPLE OUTPUT
locust.out
0
4
9
9
数据范围:
10%的数据满足W<=100,N<=100;
30%的数据满足W<=2000,N<=5000;
50%的数据满足W<=100000,N<=50000;
100%的数据满足W<=500000,N<=200000,每次蝗虫增加数不超过1000;
时间限制:
2s
分析:
CDQ分治
(听说是一个叫CDQ的前辈(♀)发明的)
学习算法的开端都是趁热打铁,抄袭代码
这是一道CDQ的入门题
题目已经简化了
大地上初始时没有蝗虫
Q:那要是有怎么办呢
A:那就当做是修改操作处理了
CDQ的一部分模型都是三维偏序
实际上就是每个元素都有三个限制因素
要依照这三个限制操作
那这道题的三个限制在哪呢
首先这是一道二维修改查询的题
提到二维区间的和,立刻想到了二维前缀和
每一个矩阵都可以由四个前缀矩阵计算出来,
那么对于每一个前缀矩阵,只有位于ta左下角的修改会对ta产生影响
(我们默认左下角为坐标原点)
而修改和查询又有一定的时间顺序
这样我们的三个限制就出来了:
1.时间
2.x坐标小于等于当前点
3.y坐标小于等于当前点
我们需要定义一个solve函数
solve(l,r)可以做到处理完所有l到r的操作
我们先按输入的时间形成一个序列
我是在只停留在理论的层次上做这道题的
一开始还在想,不是要按照输入时间排序吗,
dalao的程序为什么没有sort,
(傻吗,输入的时候顺序存储不就已经排好序了吗)
。。。sto orz
那我们在时间排序的基础上分治solve
用左区间修改右区间
先把左区间中的所有修改
和右区间的所有询问复制到一个数组里
复制出来的修改是一定会影响这些询问的
按x排序,如果x相同,插入在询问前
之后就是处理操作了
因为我们按照x排序了,所以先插入的必定会对后面前缀和有影响
那我们要怎么处理y这个限制呢
我们开一个纵向的树状数组,
维护y方向上的修改,这就相当于把整个y轴向右扫描
遇到询问就进行相应的处理
这里要注意我们把每个询问拆成了两个
毕竟确定一个询问需要两个坐标,如图
那ta的意义何在呢
之所以把询问拆成两个,是为了在排序的时候,
第一个分身排在前,第二个分身排在后,
- 如果遇到了第一个分身:
就在当前的询问中减去该询问不包含的区间值 - 遇到第二个分身的时候:
在询问中加上这一部分的区间值
最后恢复树状数组的初始状态
继续递归下去
tip
solve的时候边界是l和r
不要一个手滑写成1和n(就像某zz)
cogs真心难用。。。
这里写代码片
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200005;
int w,n,ans[N];
struct node{
int bh,t,xa,ya,xb,yb,z; //谨慎使用y1
};
node q[N],c[N<<1];
int cc,tree[500005];
int cmp(const node &a,const node &b)
{
if (a.xa==b.xa) return a.t<b.t; //插入排在询问前
return a.xa<b.xa;
}
void add(int bh,int a)
{
for (int i=bh;i<=w;i+=(i&(-i)))
tree[i]+=a;
}
int ask(int bh)
{
int ans=0;
for (int i=bh;i>0;i-=(i&(-i)))
ans+=tree[i];
return ans;
}
void solve(int l,int r)
{
if (l==r) return;
int mid=(l+r)>>1;
cc=0;
for (int i=l;i<=mid;i++)
if (q[i].t==0)
c[cc++]=q[i];
for (int i=mid+1;i<=r;i++)
if (q[i].t)
{
c[cc++]=q[i];
c[cc++]=q[i];
c[cc-2].xa--;
c[cc-1].xa=c[cc-1].xb;
c[cc-1].t=2;
}
sort(c,c+cc,cmp);
for (int i=0;i<cc;i++)
{
if (c[i].t==0) //插入
add(c[i].ya,c[i].z);
else if (c[i].t==1)
ans[c[i].bh]-=ask(c[i].yb)-ask(c[i].ya-1);
else
ans[c[i].bh]+=ask(c[i].yb)-ask(c[i].ya-1);
}
for (int i=0;i<cc;i++)
if (c[i].t==0)
add(c[i].ya,-c[i].z);
solve(l,mid);
solve(mid+1,r);
}
int main()
{
freopen("locust.in","r",stdin);
freopen("locust.out","w",stdout);
scanf("%d%d",&w,&n);
for (int i=1;i<=n;i++)
{
int x,y,xx,yy;
q[i].bh=i;
scanf("%d",&q[i].t);
q[i].t--;
if (q[i].t==0) //插入
{
scanf("%d%d%d",&q[i].xa,&q[i].ya,&q[i].z);
}
else //查询
{
scanf("%d%d%d%d",&x,&y,&xx,&yy);
q[i].xa=min(x,xx);
q[i].xb=max(x,xx);
q[i].ya=min(y,yy);
q[i].yb=max(y,yy);
}
}
solve(1,n);
for (int i=1;i<=n;i++)
if (q[i].t)
printf("%d\n",ans[i]);
return 0;
}