zoukankan      html  css  js  c++  java
  • 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的意义何在呢
    之所以把询问拆成两个,是为了在排序的时候,
    第一个分身排在前,第二个分身排在后,
    *如果遇到了第一个分身:
    就在当前的询问中减去该询问不包含的区间值
    *遇到第二个分身的时候:
    在询问中加上这一部分的区间值
    这里写图片描述

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

    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
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    深度解析MVC3中的ModelValidator及相关unobtrusiveJs的验证(一)
    深度解析Asp.net中的验证和Mvc对它的继承
    C# WebBrowser保存页面为图片
    Vue-axios需要注意的几个点
    C# 为什么说事件是一种特殊的委托
    C# event 事件-2
    C# event 事件
    初始Redis与简单使用
    泛型的运用(用于查询数据后DataTable转实体类)
    C# 初识接口 Interface
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673422.html
Copyright © 2011-2022 走看看