题目描述
摩尔瓦多的移动电话公司摩基亚(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 1≤x,y≤w。坐标的编号从 1 1 1 开始。对于一个 4 × 4 4\times 4 4×4 的正方形,就有 1 ≤ x ≤ 4 1\leq x\leq 4 1≤x≤4, 1 ≤ y ≤ 4 1\leq y\leq 4 1≤y≤4
请帮助 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 x1≤x≤x2, y 1 ≤ y ≤ y 2 y1\leq y\leq y2 y1≤y≤y2 所规定的矩形中的用户数量。 |
| 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 1≤w≤2000000
- 1 ≤ x 1 ≤ x 2 ≤ w 1\leq x1\leq x2\leq w 1≤x1≤x2≤w, 1 ≤ y 1 ≤ y 2 ≤ w 1\leq y1\leq y2\leq w 1≤y1≤y2≤w, 1 ≤ x , y ≤ w 1\leq x,y\leq w 1≤x,y≤w, 0 < a ≤ 10000 0<a\leq 10000 0<a≤10000
- 命令 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,y2−sumx1−1,y2−sumx2,y1−1+sumx1−1,y1−1
二维前缀和,这里不再多讲。
将每次查询都分成四次查询,那么所有询问都可以用命令类型和 ( 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,xj≤xi,yj≤yi
这样的问题就可以用三维偏序 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;
}
本文介绍了一个关于二维矩阵查询的问题及其解决方案。问题要求实现用户定位系统的查询功能,包括用户位置查询及指定区域内用户数量查询。通过使用二维前缀和与三维偏序CDQ分解技术,实现了高效查询。

735

被折叠的 条评论
为什么被折叠?



