cogs [BOI2007]摩基亚Mokia CDQ 分治

【题目描述】

摩尔瓦多的移动电话公司摩基亚( Mokia )设计出了一种新的用户定位系统。和其他的定位系统一样,它能够迅速回答任何形如“用户C的位置在哪?”的问题,精确到毫米。但其真正高科技之处在于,它能够回答形如“给定区域内有多少名用户?”的问题。

在定位系统中,世界被认为是一个 W×W 的正方形区域,由 1×1 的方格组成。每个方格都有一个坐标 (x,y)1<=x,y<=W 。坐标的编号从 1 开始。对于一个 4×4 的正方形,就有 1<=x<=4,1<=y<=4 (如图):

请帮助Mokia公司编写一个程序来计算在某个矩形区域内有多少名用户。

【输入格式】

有三种命令,意义如下:

命令参数意义
0W初始化一个全零矩阵。本命令仅开始时出现一次。
1x y A向方格 (x,y) 中添加A个用户。A是正整数。
2X1 Y1 X2 Y2查询 X1<=x<=X2Y1<=y<=Y2 所规定的矩形中的用户数量
3无参数结束程序。本命令仅结束时出现一次。

【输出格式】

对所有命令2,输出一个一行整数,即当前询问矩形内的用户数量。

【输入样例】

0 4

1 2 3 3

2 1 1 3 3

1 2 2 2

2 2 2 3 4

3

【输出样例】

3

5

【提示】

输入输出意义
0 4 大小为 4×4 的全零正方形
1 2 3 3  (2,3) 方格加入3名用户
2 1 1 3 3 查询矩形 1<=x<=3,1<=y<=3 内的用户数量
 3查询结果
1 2 2 2  (2,2) 方格加入2名用户
2 2 2 3 4 查询矩形 2<=x<=3,2<=y<=4 内的用户数量
 5查询结果
3 终止程序

【数据规模】

1<=W<=2000000

1<=X1<=X2<=W

1<=Y1<=Y2<=W

1<=x,y<=W

0<A<=10000

命令1不超过 160000 个。

命令2不超过 10000个。



第一个CDQ,听了已到THU的gg讲的CDQ,表示比较懵X,然后zzh大佬过来告诉我,对于一块矩阵,对他造成影响的修改的因素有两条;1:修改时间比查询时间早 2:x,y范围在此矩阵内,那么我们把query(x1,y1,x2,y2)拆成4个矩阵(x1-1,y1-1)+(x2,y2)-(x1-1,y2)-(x2,y1-1);那么我们对每个操作按x排序,这样就保证了x有序,再用一个树状数组来维护y轴,就保证了第二个条件,那么就可以分治了,在分治到时间(l,r)(其实就是第l到第r个操作)时,考虑(l,mid)中修改操作对(mid+1,r)中查询操作的影响,计算贡献,然后把时间在(l,mid)的放到前半个数组,(mid+1,r)的放在后半个数组(类似于归并排序),然后继续分治下去

然后是助以理解的code:

#define MAXN 2000005
#define lowbit(x) ((x)&(-x))
#include <bits/stdc++.h>
using namespace std;
int n,m,op,tot,cnt,Ans[MAXN],tree[MAXN];
 
 
template<typename _t>
inline _t read(){
    _t x=0;
    int f=1;
    char ch=getchar();
    for(;ch>'9'||ch<'0';ch=getchar())if(ch=='-')f=-f;
    for(;ch<='9'&&ch>='0';ch=getchar())x=x*10+(ch^48);
    return (_t)x*f;
}
 
struct pa{
    int id,op,x,y,w,pos;
    bool operator < (const pa &a)const{//修改的优先级大于查询
        if(x==a.x&&y==a.y)return op<a.op;
        return x==a.x?y<a.y:x<a.x;
    }
}a[MAXN],temp[MAXN];
 
void updata(int x,int w){
    for(;x<=n;x+=lowbit(x))tree[x]+=w;
}
 
int qsum(int x){
    int ans = 0;
    for(;x;x-=lowbit(x))ans+=tree[x];
    return ans;
}
 
void Add(){
    int x1= read<int>(),y1=read<int>();
    int x2=read<int>(),y2=read<int>();++cnt;
    a[++tot].pos=cnt;a[tot].x=x1-1;a[tot].y=y1-1;a[tot].w=1 ;a[tot].op=1;
    a[++tot].pos=cnt;a[tot].x=x2;  a[tot].y=y2;  a[tot].w=1 ;a[tot].op=1;
    a[++tot].pos=cnt;a[tot].x=x1-1;a[tot].y=y2;  a[tot].w=-1;a[tot].op=1;
    a[++tot].pos=cnt;a[tot].x=x2;  a[tot].y=y1-1;a[tot].w=-1;a[tot].op=1;
}
 
void CDQ(int l,int r){
    if(l==r)return;
    int mid=l+r>>1,l1=l,l2=mid+1;
    for(int i=l;i<=r;i++){
        if(a[i].id<=mid&&a[i].op==0)updata(a[i].y,a[i].w);
        if(a[i].id>mid&&a[i].op)Ans[a[i].pos]+=qsum(a[i].y)*a[i].w;
    }
    for(int i=l;i<=r;i++)if(a[i].id<=mid&&a[i].op==0)updata(a[i].y,-a[i].w);
    for(int i=l;i<=r;i++){
        if(a[i].id<=mid)temp[l1++]=a[i];//把时间在(l,mid)的放在前一半
        else temp[l2++]=a[i];
    }
    for(int i=l;i<=r;i++)a[i]=temp[i];
    CDQ(l,mid);CDQ(mid+1,r);
}
 
int main(){
    freopen("mokia.in","r",stdin);
    freopen("mokia.out","w",stdout);
    n=read<int>();n=read<int>();
    while(1){
        op=read<int>();
        if(op==1){
            a[++tot].x=read<int>();
            a[tot].y=read<int>();
            a[tot].w=read<int>();
        }
        else if(op==2)Add();
        else break;
    }
    for(int i=1;i<=tot;i++)a[i].id=i;
    sort(a+1,a+1+tot);
    CDQ(1,tot);
    for(int i=1;i<=cnt;i++)printf("%d\n",Ans[i]);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值