hdu6183 Color it(动态开点线段树)

题意:

有一个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棵树太多了,动态开点.
code:
#include<bits/stdc++.h>
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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值