P4390 [BOI2007] Mokia

本文介绍了一个关于二维矩阵查询的问题及其解决方案。问题要求实现用户定位系统的查询功能,包括用户位置查询及指定区域内用户数量查询。通过使用二维前缀和与三维偏序CDQ分解技术,实现了高效查询。

P4390 [BOI2007]Mokia

题目描述

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

在定位系统中,世界被认为是一个 w × w w×w w×w 的正方形区域,由 1 × 1 1\times 1 1×1 的方格组成。每个方格都有一个坐标 ( x , y ) (x,y) (x,y) 1 ≤ x , y ≤ w 1\leq x,y\leq w 1x,yw。坐标的编号从 1 1 1 开始。对于一个 4 × 4 4\times 4 4×4 的正方形,就有 1 ≤ x ≤ 4 1\leq x\leq 4 1x4 1 ≤ y ≤ 4 1\leq y\leq 4 1y4

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

输入格式

有三种命令,意义如下:

命令参数意义
0 0 0 w w w初始化一个全零矩阵。本命令仅开始时出现一次。
1 1 1 x   y   a x\,y\,a xya向方格 ( x , y ) (x,y) (x,y) 中添加 a a a 个用户。 a a a 是正整数。
2 2 2 x 1   y 1   x 2   y 2 x1\,y1\,x2\,y2 x1y1x2y2查询 x 1 ≤ x ≤ x 2 x1\leq x\leq x2 x1xx2 y 1 ≤ y ≤ y 2 y1\leq y\leq y2 y1yy2 所规定的矩形中的用户数量。
3 3 3无参数结束程序。本命令仅结束时出现一次。

输入共若干行,每行有若干个整数,表示一个命令。

输出格式

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

样例
样例输入
0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
样例输出
3
5
数据范围

对于 100 % 100\% 100% 的数据

  • 1 ≤ w ≤ 2000000 1\leq w\leq 2000000 1w2000000
  • 1 ≤ x 1 ≤ x 2 ≤ w 1\leq x1\leq x2\leq w 1x1x2w 1 ≤ y 1 ≤ y 2 ≤ w 1\leq y1\leq y2\leq w 1y1y2w 1 ≤ x , y ≤ w 1\leq x,y\leq w 1x,yw 0 < a ≤ 10000 0<a\leq 10000 0<a10000
  • 命令 1 1 1 不超过 160000 160000 160000
  • 命令 2 2 2 不超过 10000 10000 10000

题解

题目大意就是给一个 w × w w\times w w×w的矩阵,以及若干次操作,操作分两类,单点修改和区间查询。

首先,我们对区间查询做一些小小的修改。假设要查询矩阵 ( x 1 , y 1 , x 2 , y 2 ) (x_1,y_1,x_2,y_2) (x1,y1,x2,y2),设 s u m i , j sum_{i,j} sumi,j表示矩阵 ( 1 , 1 , i , j ) (1,1,i,j) (1,1,i,j)的权值和,那么要查询的矩阵的权值和为:

s u m x 2 , y 2 − s u m x 1 − 1 , y 2 − s u m x 2 , y 1 − 1 + s u m x 1 − 1 , y 1 − 1 sum_{x_2,y_2}-sum_{x_1-1,y_2}-sum_{x_2,y_1-1}+sum_{x_1-1,y_1-1} sumx2,y2sumx11,y2sumx2,y11+sumx11,y11

二维前缀和,这里不再多讲。

将每次查询都分成四次查询,那么所有询问都可以用命令类型和 ( x i , y i ) (x_i,y_i) (xi,yi)来表示了。对于每次查询 ( x i , y i ) (x_i,y_i) (xi,yi) s u m x i , y i sum_{x_i,y_i} sumxi,yi的值为满足以下条件的 j j j的个数:

j < i , x j ≤ x i , y j ≤ y i j<i,x_j \leq x_i,y_j \leq y_i j<i,xjxi,yjyi

这样的问题就可以用三维偏序 c d q cdq cdq来解决了。

最后将各询问分成的四个询问按前面的式子组合起来,即可得到最后答案。总时间复杂度为 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

code

#include<bits/stdc++.h>
using namespace std;
int n,tp,tot,qt;
long long s,ans[100005],tr[2000005];
struct node{
    int x,y,tp,v,id;
    long long ans;
    bool operator<(const node ax)const{
        if(x!=ax.x) return x<ax.x;
        if(y!=ax.y) return y<ax.y;
        return ax.tp!=0;
    }
}a[200005],b[200005];
bool cmp1(node ax,node bx){
    return ax.id<bx.id;
}
int lb(int i){
    return i&(-i);
}
void pt(int i,long long t){
    while(i<=n){
        tr[i]+=t;i+=lb(i);
    }
}
long long find(int i){
    long long re=0;
    while(i){
        re+=tr[i];i-=lb(i);
    }
    return re;
}
void cdq(int l,int r){
    if(l==r) return;
    int mid=(l+r)/2;
    cdq(l,mid);cdq(mid+1,r);
    sort(a+l,a+mid+1);sort(a+mid+1,a+r+1);
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r){
        if(a[i]<a[j]){
            if(a[i].tp==0) pt(a[i].y,a[i].v);
            b[k]=a[i];++i;++k;
        }
        else{
            if(a[j].tp) a[j].ans+=find(a[j].y);
            b[k]=a[j];++j;++k;
        }
    }
    while(i<=mid){
        if(a[i].tp==0) pt(a[i].y,a[i].v);
        b[k]=a[i];++i;++k;
    }
    while(j<=r){
        if(a[j].tp) a[j].ans+=find(a[j].y);
        b[k]=a[j];++j;++k;
    }
    for(int o=l;o<=mid;o++)
    if(a[o].tp==0) pt(a[o].y,-a[o].v);
    for(int o=l;o<=r;o++){
        a[o]=b[o];
    }
}
int main()
{
    scanf("%lld%d",&s,&n);
    for(int i,j,x,y,v;;){
        scanf("%d",&tp);
        if(tp==3) break;
        if(tp==1){
            scanf("%d%d%d",&x,&y,&v);
            a[++tot]=(node){x,y,0,v,tot,0};
        }
        else{
            scanf("%d%d%d%d",&i,&j,&x,&y);
            ans[++qt]=s*(x-i+1)*(y-j+1);
            if(i>1&&j>1) a[++tot]=(node){i-1,j-1,1,qt,tot,0};
            if(i>1) a[++tot]=(node){i-1,y,-1,qt,tot,0};
            if(j>1) a[++tot]=(node){x,j-1,-1,qt,tot,0};
            a[++tot]=(node){x,y,1,qt,tot,0};
        }
    }
    cdq(1,tot);
    for(int i=1;i<=tot;i++){
        if(a[i].tp){
            ans[a[i].v]+=a[i].ans*a[i].tp;
        }
    }
    for(int i=1;i<=qt;i++) printf("%lld\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值