UVa OJ 10587/POJ 2528

1、经典的离散化题目,用线段树加速。

2、离散化就是将无限空间(或很大空间)的点映射到有限区域(或很小区域),说得通俗点就是“只保存我们需要的一些点”,一般指区间端点。具体见注释。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
const int MAX=20010;
using namespace std;
int c,n,ls[MAX];
struct node{
    int l,r;
 int c;
}tree[MAX*4];
struct ln{
    int li,num;
}line[MAX];
int set[MAX][2];
bool vis[MAX];
int ans,tp;
bool cmp(struct ln a,struct ln b){
    return a.li<b.li;
}
void Inittree(int pos,int ll,int rr){
    tree[pos].l=ll;
 tree[pos].r=rr;
 tree[pos].c=0;//将着色数清零
 if(ll!=rr){
     int mid=(ll+rr)/2;
        Inittree(pos*2,ll,mid);
  Inittree(pos*2+1,mid+1,rr);
 }
}
void Insert(int pos,int ll,int rr,int color){
    if(tree[pos].l>=ll&&tree[pos].r<=rr){//若所要修改的区间覆盖该线段,直接修改返回即可。
  tree[pos].c=color;
     return;
 }
    if(tree[pos].c>0&&tree[pos].c!=color){ //如果已经着了不同颜色,将lazy标记向下传,自己颜色标记为0。
     tree[pos*2].c=tree[pos].c;
  tree[pos*2+1].c=tree[pos].c;
  tree[pos].c=0;
 }
 int mid=(tree[pos].l+tree[pos].r)/2;
 if(rr<=mid){
     Insert(pos*2,ll,rr,color);//在左边覆盖
 }else if(ll>mid){
     Insert(pos*2+1,ll,rr,color);//在右边覆盖
 }else{
     Insert(pos*2,ll,mid,color);//如果横跨中点,在两边覆盖
  Insert(pos*2+1,mid+1,rr,color);
 }
}
void Search(int pos){
  if(pos>=4*tp) return;//超界了直接返回
     if(tree[pos].c!=0){ 
      if(!vis[tree[pos].c]){//若有颜色(单色)且未访问,色数加一,返回
    vis[tree[pos].c]=true;
       ans++;
   }
   return;
  }
  Search(2*pos);//若是混合色,访问两边
  Search(2*pos+1);
}
int main(){
     scanf("%d",&c);
  while(c--){
      scanf("%d",&n);
         for(int i=0;i<n;i++){
    scanf("%d%d",&set[i][0],&set[i][1]);//读入初始的区间端点,等下还要调整(等下保存编号)
       line[2*i].li=set[i][0];//保存真实坐标
       line[2*i].num=-(i+1);//其绝对值为所在区间编号,若是区间左端点为负,右端点为正
       line[2*i+1].li=set[i][1];
             line[2*i+1].num=i+1;
   }
   sort(line,line+2*n,cmp);//由于数据是任意顺序输入的,所以要按照真实坐标排成不降序
         int temp=line[0].li;
   tp=1;
   for(int i=0;i<2*n;i++){
       if(line[i].li!=temp){
        tp++;
     temp=line[i].li;
    }//去重,tp保存不同的坐标数
    if(line[i].num<0){
     set[-line[i].num-1][0]=tp;//赋予新编号,也就是映射后的下标
             }
    else{
        set[line[i].num-1][1]=tp;
       }
   }
   Inittree(1,1,tp);//初始化线段树
         for(int i=0;i<n;i++)
    Insert(1,set[i][0],set[i][1],i+1);//修改线段颜色,四个参数分别是:初始树结点编号,左端点映射值,右端点映射值,颜色编号
   memset(vis,0,sizeof(vis));
   ans=0;
   Search(1);
   printf("%d\n",ans);
  }
     return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值