有一个1e6*1e6的二维坐标系.
有4种操作:
(0):清空所有点
(1,x,y,c):在(x,y)处添加一个颜色为c的点
(2,x,y1,y1):查询横坐标[1,x]内,纵坐标[y1,y2]内有多少种颜色不同的点
(3):退出
数据范围:操作1操作2加起来最多150000次,操作0最多10次,0<=c<=50
设左下角为[1,1],右上角为[1e6,1e6]
这题的关键点:
每次查询的矩阵都是左下角的一块,
因为只有添加点的操作,没有删除点的操作,
那么对于同一纵坐标y,如果同时存在[x,y]和[x+1,y],
显然[x,y]比[x+1,y]更优,那么只存储[x,y]即可.
对每个颜色开一棵线段树,每个叶子节点是一个纵坐标,
每个节点存储该纵坐标下最小的横坐标,
查询就是在每种颜色的树上查询[y1,y2]内是否存在<=x的值,
维护一下区间min用来剪枝.
50棵树太多了,动态开点.
#include
using namespace std;
const int maxm=1e6+5;
int lc[maxm],rc[maxm],mi[maxm];
int rt[55],tot;
void init(){//清空
tot=0;
for(int i=0;i<=50;i++)rt[i]=0;
}
void pushup(int k){
mi[k]=min(mi[lc[k]],mi[rc[k]]);
}
void update(int x,int val,int l,int r,int &k){
if(!k)k=++tot,lc[k]=rc[k]=0,mi[k]=1e9;
if(l==r){
mi[k]=min(mi[k],val);
return ;
}
int mid=(l+r)/2;
if(x<=mid)update(x,val,l,mid,lc[k]);
else update(x,val,mid+1,r,rc[k]);
pushup(k);
}
int ask(int st,int ed,int val,int l,int r,int k){
if(!k)return 0;
if(mi[k]>val)return 0;
if(st<=l&&ed>=r)return mi[k]<=val;
int mid=(l+r)/2;
int ans=0;
if(st<=mid)ans|=ask(st,ed,val,l,mid,lc[k]);
if(ed>mid&&!ans)ans|=ask(st,ed,val,mid+1,r,rc[k]);
return ans;
}
signed main(){
mi[0]=1e9;
const int n=1e6;
int op;
while(scanf("%d",&op)!=EOF&&op!=3){
if(op==0){
init();
}else if(op==1){//插入
int x,y,c;scanf("%d%d%d",&x,&y,&c);
update(y,x,1,n,rt[c]);
}else if(op==2){//查询
int x,y1,y2;scanf("%d%d%d",&x,&y1,&y2);
int ans=0;
for(int i=0;i<=50;i++){
ans+=ask(y1,y2,x,1,n,rt[i]);
}
printf("%d\n",ans);
}
}
return 0;
}