BZOJ 1176 Mokia(cdq分治,解决一类在线查询问题)

题意:维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.

这题在bz是贵族题= =,没有链接了

解法:cdq分治,第一维是时间t,第二维是x,第三维是y;每个操作看作一个三维序(t, x, y);假设修改操作是(t, x, y),两个查询操作分别是(t1, x1, y1) 和 (t1, x2, y2);实际上就要问,有多少个修改操作,满足t<=t1,x1<=x<=x2,y1<=y<=y2;这个用cdq分治就能做了,要将查询拆成四个点,利用容斥原理来做。不愿意细讲了= =,具体看代码吧

My code

//Hello. I‘m Peter.
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<bitset>
#include<set>
#include<map>
using namespace std;
#define peter cout<<"i am peter"<<endl
#define fuck(x) cerr << #x << " <- " << x << endl
typedef long long ll;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}

#define N 200010
int w,n,li[N],nli,ans[N];
struct Pair{
    int ty, id;
    int x, y, v;
    Pair(){};
    Pair(int ty1,int id1,int x1,int y1,int v1){
        ty = ty1, id = id1,x = x1, y = y1, v = v1;
    }
}p[N],s1[N],s2[N];
bool cmp1(const Pair a, const Pair b){
    return a.x < b.x;
}

int sum[N];
void add(int x,int v){
    for(int i = x; i <= nli; i += i&-i){
        sum[i] += v;
    }
}
int query(int x){
    int ans = 0;
    for(int i = x; i > 0; i -= i&-i){
        ans += sum[i];
    }
    return ans;
}
void clear(int x){
    for(int i = x; i <= nli; i += i&-i){
        sum[i] = 0;
    }
}

void cdq(int l,int r){
    if(l == r) return;
    int mid = (l + r) >> 1;
    cdq(l, mid);

    int n1 = 0, n2 = 0;
    for(int i = l; i <= mid; i++) if(p[i].ty == 1) s1[++n1] = p[i];
    for(int i = mid + 1; i <= r; i++) if(p[i].ty == 2) s2[++n2] = p[i];
    sort(s1 + 1, s1 + 1 + n1, cmp1);
    sort(s2 + 1, s2 + 1 + n2, cmp1);

    int t1 = 1;
    for(int t2 = 1; t2 <= n2; t2++){
        for(;t1 <= n1 && s1[t1].x <= s2[t2].x; t1++){
            add(s1[t1].y, s1[t1].v);
        }
        int id = s2[t2].id;
        ans[id] += s2[t2].v * query(s2[t2].y);
    }
    for(int i = 1; i < t1; i++){
        clear(s1[i].y);
    }

    cdq(mid + 1, r);
}

bool isq[N];
int nop;
int main(){
    int t; t = read();
    w = read();
    for(int i = 1; ;i++){
        t = read();
        if(t == 3) break;

        nop = nop + 1;
        if(t == 1){
            isq[i] = 0;
            int x,y,v;
            x = read(), y = read(), v = read();
            p[++n] = Pair(t, i, x, y, v);
            li[++nli] = y;
        }
        else if(t == 2){
            isq[i] = 1;
            int x1,y1,x2,y2;
            x1 = read(), y1 = read(), x2 = read(), y2 = read();
            p[++n] = Pair(t, i, x2, y2, +1);
            p[++n] = Pair(t, i, x2, y1 - 1, -1);
            p[++n] = Pair(t, i, x1 - 1, y2, -1);
            p[++n] = Pair(t, i, x1 - 1, y1 - 1, +1);
            li[++nli] = y2;
            li[++nli] = y1 - 1;
        }
    }

    sort(li + 1, li + 1 + nli);
    nli = (int)(unique(li + 1, li + 1 + nli) - (li + 1));
    for(int i = 1; i <= n; i++){
        p[i].y = (int)(lower_bound(li + 1, li + 1 + nli, p[i].y) - li);
    }

    for(int i = 1; i <= nli; i++) sum[i] = 0;
    for(int i = 1; i <= nop; i++) ans[i] = 0;
    cdq(1, n);

    for(int i = 1; i <= nop; i++){
        if(isq[i]) printf("%d\n",ans[i]);
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-19 20:19:21

BZOJ 1176 Mokia(cdq分治,解决一类在线查询问题)的相关文章

[bzoj] 1176 Mokia || CDQ分治

原题 给出W×W的矩阵(S没有用,题目有误),给出无限次操作,每次操作的含义为: 输入1:你需要把(x,y)(第x行第y列)的格子权值增加a 输入2:你需要求出以左下角为(x1,y1),右上角为(x2,y2)的矩阵内所有格子的权值和,并输出 输入3:表示输入结束 因为修改之间相互独立,所以可以用CDQ. 三个维度分别为时间,x轴,y轴. 简单的三维偏序即可. #include<cstdio> #include<algorithm> #define N 100010 #define

[用CDQ分治解决区间加&amp;区间求和]【习作】

[前言] 作为一个什么数据结构都不会只会CDQ分治和分块的蒟蒻,面对区间加&区间求和这么难的问题,怎么可能会写线段树呢 于是,用CDQ分治解决区间加&区间求和这篇习作应运而生 [Part.I]区间加&区间求和的数据结构做法 [一]线段树 裸题... 1141ms #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include

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 Balkan 2007 Mokia CDQ分治

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

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 Mokia(CDQ分治)

[题目链接]  http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=96974 [题意] 定义查询操作与修改操作:1 x y z 为将格子(x,y)修改为z:2 x1 y1 x2 y2为查询以(x1,y1)为左上(x2,y2)为右下的子矩阵之和. [思路] cdq分治. 矩阵初始值都为s,所以先不考虑. 首先明确每一个修改操作都互不影响.然后把每一个对子矩阵的询问操作差分为对四个点的“前缀和”操作. 先把所有的操作按照x升序排列

BZOJ 3262: 陌上花开 [CDQ分治 三维偏序]

Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要美丽,当且仅当Sa>=Sb,Ca>=Cb,Ma>=Mb.显然,两朵花可能有同样的属性.需要统计出评出每个等级的花的数量. Input 第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值. 以下N行,每

BZOJ 1492 货币兑换 cdq分治或平衡树维护凸包

题意:链接 方法:cdq分治或平衡树维护凸包 解析: 这道题我拒绝写平衡树的题解,我仅仅想说splay不要写挂,insert边界条件不要忘.del点的时候不要脑抽d错.有想写平衡树的去看140142或者留言我. 首先这道题能推出个表达式 f[i]代表第i天最大收益. xx[i]表示将第i天的钱都买A的数量 yy[i]表示将第i天的钱都买B的数量 所以f[i]=max(f[i?1],p[i].a?xx[j]+p[i].b?yy[j])j<i 所以我们要维护这个n^2的递推式 又知道f[i]是由小于

BZOJ 1176 MOKIA

cdq分治. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 2000500 #define maxq 400050 using namespace std; int s,w,type,a,b,c,d,cnt=0,m=0,ans[maxq]; struct ques { int val,opt,x,y,pos,id; }q[maxq]