cogs577. 蝗灾(CDQ)

★★★☆
输入文件: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的意义何在呢
之所以把询问拆成两个,是为了在排序的时候,
第一个分身排在前,第二个分身排在后,

  • 如果遇到了第一个分身:
    就在当前的询问中减去该询问不包含的区间值
  • 遇到第二个分身的时候:
    在询问中加上这一部分的区间值
    cogs577. 蝗灾(CDQ)_第1张图片

最后恢复树状数组的初始状态
继续递归下去

tip

solve的时候边界是l和r
不要一个手滑写成1和n(就像某zz)

cogs真心难用。。。

这里写代码片
#include
#include
#include
#include

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//插入排在询问前 
    return a.xaint 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;iif (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;iif (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;
}

你可能感兴趣的:(CDQ分治)