码迷,mamicode.com
首页 > 其他好文 > 详细

pku2528----区间染色成段更新

时间:2015-03-29 10:51:38      阅读:177      评论:0      收藏:0      [点我收藏+]

标签:

     //   Creat   By  郭仔    2012年3月29日10:16:59


区间染色的变形,不过比区间染色问题要难一些~

用到区间染色成段更新,hash,离散化,蛋疼的提

题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报
思路:这题数据范围很大,直接搞超时+超内存,需要离散化:
离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来了
所以离散化要保存所有需要用到的值,排序后,分别映射到1~n,这样复杂度就会小很多很多
而这题的难点在于每个数字其实表示的是一个单位长度(并且一个点),这样普通的离散化会造成许多错误poj这题数据奇弱)
给出下面两个简单的例子应该能体现普通离散化的缺陷:
1-10 1-4 5-10
1-10 1-4 6-10
为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.

技术分享还是不知道为啥会出现这种情况

————————————————————————————————————————————————————————
假设给定的区间是[1,3],[6,1000],我们可以如下离散
离散前坐标:1 3 6 1000
离散后坐标:1 2 3 4
于是我们就减少了没必要的搜索过程和内存空间。有个建树时的小技巧,因为我建树的每个节点是开区间到闭区间,即[a,b)。于是在读入数据的时候我就可以把b的值加一,这样就很好的处理了题目中可能出现的[a,a]相等值的区间(也就是对一个点的处理)。

根据这一点可以更改下面的程序的cout,不用加1,而在输入值时区间右边界加1就行

一下来自http://chenjiapeng.blogbus.com/logs/72973396.html

现在做个小小的总结:

1.线段树不会有固定的模板,只是种思想,一般可能都需要离散化。

2.分成建树,染色,判断统计3个步骤,可能其他大致也就3个类似的步骤。

3.如果有单个点的情况,建区间时可以把右端点+1处理。

有很多还不是很清楚,等以后具体研究时再去发掘吧。

/*线段树*/
#include <iostream>
#include <algorithm>
using namespace std;
const int Max=20010;
struct point//记录n个区间
{
 int l;
 int r;
}a[Max];
struct Node
{
 int l;
 int r;
 int mid;
 int color;
}tree[Max*4];
int t,n;
int hash[Max*2];//哈希是种态度  = = 
bool used[Max];//最后统计单颜色的区间
void make(int l,int r,int num)//构建线段树
{
 tree[num].l = l;
 tree[num].r = r;
 tree[num].mid=(l+r)/2;
 tree[num].color=0;
 if(l+1!=r)//不是叶区间
 {
  make(l,tree[num].mid,num*2);
  make(tree[num].mid,r,num*2+1);
  return;
 }
}
/*染色函数*/
void insert(int num,int l,int r,int c)//节点编号num. 要染色的区间
{                                     //的左右端点l,r和颜色c
 if(tree[num].color!=c)//区间的颜色不是所要染得色c
 {                     //如果是c,说明已经上满了c
  if(tree[num].l==l&&tree[num].r==r)//区间完全覆盖,给该区间染上c
  {
   tree[num].color=c;
   return;
  }

  //以下是区间未被完全覆盖的情况

  if(tree[num].color>=0)//区间未被上色或者之上一种色(这个颜色是上满的) 
  {                   //那么其子节点 也是这个颜色
   tree[2*num].color=tree[num].color;
   tree[2*num+1].color=tree[num].color;
   tree[num].color=-1;//由于一开始有颜色(或者没有)
   //上色的c只占区间的一部分,所以是杂色
  }
  if(r<=tree[num].mid)//染色区间在该区间的左子区间
   insert(2*num,l,r,c);
  else if(l>=tree[num].mid)//染色区间在该区间的右子区间
   insert(2*num+1,l,r,c);
  else//染色区间横跨区间的左右区间
  {
   insert(num*2,l,tree[num].mid,c);
   insert(num*2+1,tree[num].mid,r,c);
  }
 }
}
/*统计函数
统计最后的颜色区间*/
void count(int num)
{
 if(tree[num].color>0)
 {
  used[tree[num].color]=true;
  return ;
 }
 if(tree[num].l+1!=tree[num].r)
 {
  count(2*num);
  count(2*num+1);
 }
}
/*查找函数
val是未离散化前面的值。
函数返回的是其在离散化后的区间的编号*/
int b_search(int l,int r,int val)
{
 while(l<=r)
 {
  int mid=(l+r)/2;
  if(hash[mid]==val)
   return mid;
  else if(hash[mid]>val)
   r=mid-1;
  else
   l=mid+1;
 }
 return -1;
}
int main()
{
 scanf("%d",&t);
 for(int i=1;i<=t;i++)
 {
  scanf("%d",&n);
  memset(used,false,sizeof(used));
  for(int j=1;j<=n;j++)
  {
   scanf("%d %d",&a[j].l,&a[j].r);
   ++a[j].r;
   hash[2*j-1]=a[j].l;
   hash[2*j]=a[j].r;
  }
  sort(hash+1,hash+1+2*n);
  int index=2;
  for(int j=1;j<2*n;j++)
   if(hash[j]!=hash[j+1])
    hash[index++]=hash[j+1];
  make(1,index-1,1);
  for(int j=1;j<=n;j++)
  {
   int lset=b_search(1,index-1,a[j].l);
   int rset=b_search(1,index-1,a[j].r);
   insert(1,lset,rset,j);
  }
  count(1);
  int ans=0;
  for(int j=0;j<Max;j++)
   if(used[j])
    ans++;
  cout<<ans<<endl;
 }
 return 0;
}


pku2528----区间染色成段更新

标签:

原文地址:http://blog.csdn.net/guoxianzhuang/article/details/44724795

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!