BZOJ 1176: [Balkan2007]Mokia

一道CDQ分治的模板题,然而我De了一上午Bug......

按时间分成左右两半,按x坐标排序然后把y坐标丢到树状数组里,扫一遍遇到左边的就add,遇到右边的query

几个弱智出了bug的点,

一是先分了左右两半再排序,保证的是这次的左右是上次没有计算过的贡献,

for(int i=l;i<=r;i++) qs[i].k=(i>mid);
    sort(qs+l,qs+r+1,cmp2);

然后时间的先后是因为一开始就是按时间排好序的已经保证了。

二是矩阵的一个经典的套路就是拆成四部分差分查询。

三是很智障的,树状数组要注意y==0的情况

四是比较重要的,比较的时候条件要加充分,之前写陌上花开也出现了这个问题,一维还是两维相同就不管了,会爆炸。。。一定要考虑清楚所有会影响的维,都要加入排序条件中

bool cmp2(const node&a,const node&b) {
    return a.x<b.x||(a.x==b.x&&(a.y<b.y||(a.y==b.y&&bo[a.id]<bo[b.id])));
}

还有,数组大小要开够,在下RE好像大半都是数组问题。。

//Twenty
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#define mid ((l+r)>>1)
using namespace std;
const int maxn=200000+299;
const int maxw=2000000+29;
int tot,s,w,opt,x,y,x2,y2,a,sg[maxw],bo[maxn],ans,cnt;
struct node{
    int x,y,v,id,sum,k;
    node(){sum=0;};
    node(int x,int y,int v,int id):x(x),y(y),v(v),id(id){sum=0;}
}qs[maxn];
bool cmp1(const node&a,const node&b) {
    return a.id<b.id;
}
bool cmp2(const node&a,const node&b) {
    return a.x<b.x||(a.x==b.x&&(a.y<b.y||(a.y==b.y&&bo[a.id]<bo[b.id])));//!!!!!!!
}
void add(int x,int v) {
    for(int i=x;i<=w;i+=(i&(-i)))
       sg[i]+=v;
}
int qry(int x) {
    int res=0;
    for(int i=x;i>0;i-=(i&(-i)))
        res+=sg[i];
    return res;
}
void solve(int l,int r) {
    if(l==r) return ;
    solve(l,mid); solve(mid+1,r);
    for(int i=l;i<=r;i++) qs[i].k=(i>mid);
    sort(qs+l,qs+r+1,cmp2);
    for(int i=l;i<=r;i++) {
        if(bo[qs[i].id]&&qs[i].k) {
            if(qs[i].y) qs[i].sum+=qry(qs[i].y);
        }
        else if(!bo[qs[i].id]&&!qs[i].k) {
            if(qs[i].y ) add(qs[i].y,qs[i].v);
        }
    }
    for(int i=l;i<=r;i++)
        if(!bo[qs[i].id]&&!qs[i].k) {
            if(qs[i].y) add(qs[i].y,-qs[i].v);
        }
}
int main()
{
    scanf("%d%d",&s,&w);
    for(;;) {
        scanf("%d",&opt);
        if(opt==3) break;
        if(opt==1) {
            scanf("%d%d%d",&x,&y,&a);
            qs[++tot]=node(x,y,a,tot);
        }
        else {
            scanf("%d%d%d%d",&x,&y,&x2,&y2);
            qs[++tot]=node(x-1,y-1,1,tot);
            qs[++tot]=node(x-1,y2,-1,tot-1);
            qs[++tot]=node(x2,y-1,-1,tot-2);
            qs[++tot]=node(x2,y2,1,tot-3);
            bo[tot-3]=1;//是询问
        }

    }
    solve(1,tot);
    sort(qs+1,qs+tot+1,cmp1);
    for(int i=1;i<=tot;i++)
    if(bo[qs[i].id]){
        cnt++;
        ans+=qs[i].sum*qs[i].v;
        if(cnt==4) {
            printf("%d\n",ans);
            ans=0,cnt=0;
          }
    }
    return 0;
}

1176: [Balkan2007]Mokia

吐槽一句为什么两道模板都是权限题呀。。

时间: 2024-10-24 12:35:32

BZOJ 1176: [Balkan2007]Mokia的相关文章

bzoj 1176 [Balkan2007]Mokia - CDQ分治 - 树状数组

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 输入

BZOJ 1176 [Balkan2007]Mokia CDQ分治

题目大意: 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. POJ1195的加强版 没记错的话上午这题还没有中文题目描述的说0.0 好迅速 首先这题看到W就知道二维树状数组挂了 看到M就发现离散化了也搞不了 0.0 这题似乎是CDQ分治被发现之后第二个解决的题目...不过只有会员才知道的世界,今天反应过来刷刷... 修改和询问放在一起分治,一个询问拆分成4个,树状数组处

【BZOJ】1176: [Balkan2007]Mokia(cdq分治)

http://www.lydsy.com/JudgeOnline/problem.php?id=1176 在写这题的时候思维非常逗啊........2333................... 最后不得不去看别人的代码.. 噗,,我怎么没想到二维前缀和.................... orz zyf 那么对于一个矩形,我们拆成四个点,那么就可以和add操作一起cdq分治! orz cdq分治的话很好想的: 定义$solve(l, r)$表示用l~mid来更新mid+1~r. 考虑如何$

【BZOJ 1176】 [Balkan2007]Mokia

1176: [Balkan2007]Mokia Time Limit: 30 Sec  Memory Limit: 162 MB Submit: 736  Solved: 306 [Submit][Status] Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. Input 第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小 接下来每行为一下

[BZOJ1176][Balkan2007]Mokia cdq+树状数组

1176: [Balkan2007]Mokia Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 3134  Solved: 1395[Submit][Status][Discuss] Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. Input 第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小

【BZOJ 1176】【Balkan 2007】Mokia

http://www.lydsy.com/JudgeOnline/problem.php?id=1176 整体二分的例题 把每个询问拆成四个询问,整体二分里x坐标递增,按x坐标扫的时候用树状数组维护y坐标前缀和. 一开始想复杂了,按cdq分治先solve左边再处理中间再solve右边,这样每次都要对x坐标排序,常数巨大,T了好几次TwT 后来参考了别人的代码,发现自己一开始就想复杂了.这道题不需要在solve完后还是保持原来的按x坐标递增的顺序,也不需要先处理出左边的信息才能更新右边的信息. 这

BZOJ 1176 Balkan 2007 Mokia CDQ分治

题目大意:有一些操作,给一个坐标代表的点加上一个数,和求出一个矩形中的所有数的和. 思路:一眼题,二位树状数组水过. ... .. . 哪里不对?W<=2000000.逗我?这n^2能开下? 这个时候CDQ神牛又来帮助我们了. 这个题应该算是CDQ分治的模板题了吧,简单分析一下,其实不难. 写这个题之前建议写一下BZOJ 1935 SHOI 2007 Tree 园丁的烦恼 树状数组这个题,是本题的简化版. 按照正常的解法,我们应该建立一个二位的数据结构,然后分别维护两维的信息.如果用动态开点的线

[BZOJ 1176&amp;COGS 1752][BOI2007]Mokia(CDQ分治)

Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. Solution 这道在BZOJ上是权限题啊…然而我在cogs上找到了它 依旧是CDQ分治的论文题,CDQ太强辣… 把查询拆成四个前缀和的形式,在CDQ分治中用时间把操作分为两部分,保证两部分的x各自单调(只有x较小的操作才能对较大的操作产生影响),然后用树状数组维护y这一维 #include<

bzoj千题计划144:bzoj1176: [Balkan2007]Mokia

http://www.lydsy.com/JudgeOnline/problem.php?id=1176 CDQ分治 #include<cstdio> #include<iostream> #include<algorithm> #define lowbit(x) x&-x using namespace std; #define N 160001 #define M 10001 typedef long long LL; int w; LL c[2000001