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

//   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;

}

时间: 2024-10-08 15:20:21

pku2528----区间染色成段更新的相关文章

POJ训练计划2777_Count Color(线段树/成段更新/区间染色)

解题报告 题意: 对线段染色,询问线段区间的颜色种数. 思路: 本来直接在线段树上染色,lz标记颜色.每次查询的话访问线段树,求出颜色种数.结果超时了,最坏的情况下,染色可以染到叶子节点. 换成存下区间的颜色种数,这样每次查询就不用找到叶子节点了,用按位或来处理颜色种数. #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace

Count the Colors(线段树之区间成段更新)

萌萌哒的传送门 这道题目是线段树区间成段更新的应用,我们只需在建立线段树时从原来的左右儿子不相连,改为相连即可以解决此类问题. 如从原来的[l,mid] , [mid + 1,r] 改为 [l,mid],[mid,r]即可; /********************* * zoj1610 * * 线段树的区间成段更新 * 延迟标记 * *********************/ #include <iostream> #include <cstring> #include &l

hdu 3379 Sequence operation(成段更新,区间合并)

http://acm.hdu.edu.cn/showproblem.php?pid=3397 线段树很好的题.涉及到的知识点:lazy操作处理异或操作和置01,区间合并. 有五种操作: 0 a b 将[a,b]变为0 1 a b 将[a,b]变为1 2 a b 将[a,b]取反 3 a b 输出[a,b]的1的个数 4 a b 输出[a,b]内最长的连续1的个数 对区间的操作与poj 3225类似.置01与取反是互斥的,一段区间上只能有一个标记,discuss里面也说了取反的时候若递归到区间全为

NYOJ 1068 ST(线段树之 成段更新+区间求和)

ST 时间限制:1000 ms  |  内存限制:65535 KB 难度:1 描述 "麻雀"lengdan用随机数生成了后台数据,但是笨笨的他被妹纸的问题给难住了... 已知lengdan生成了N(1=<N<=10005)个随机整数,妹子对这些数可能有以下几种操作或询问: 1,A a b c 表示给区间a到b内每个数都加上c: 2,S a b  表示输出区间a到b内的和: 3,Q a b 表示区间a到b内的奇数的个数: 为了使妹纸不口渴,所以我们决定妹纸的询问次数少一点,即

HDU 3397 Sequence operation(线段树&#183;成段更新&#183;区间合并&#183;混合操作)

题意  给你一个只有0, 1的数组  有这些操作 0. 将[a, b]区间的所有数都改为0 1. 将[a, b]区间的所有数都改为1 2. 将[a, b]区间的所有数都取反 即与1异或 3. 输出区间[a, b]中1的个数  即所有数的和 4. 输出区间[a, b]中最大连续1的长度 对于所有的3, 4操作输出对应的答案 单个的操作都很简单  但搞在一起就有点恶心了  还好数组里的数只有0和1 线段树维护9个值 对应区间0, 1的最大长度len[i]  对应区间左端点为起点的最大0, 1长度ll

POJ 3468-A Simple Problem with Integers(线段树:成段更新,区间求和)

A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 62228   Accepted: 19058 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of

HDU 3397 Sequence operation (线段树,成段更新,区间合并)

http://acm.hdu.edu.cn/showproblem.php?pid=3397 Sequence operation Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5801    Accepted Submission(s): 1713 Problem Description lxhgww got a sequence

ACM: Copying Data 线段树-成段更新-解题报告

Copying Data Time Limit:2000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Description We often have to copy large volumes of information. Such operation can take up many computer resources. Therefore, in this problem you are advised to come

ZOJ1610_Count the Colors(线段树/成段更新)

解题报告 题意: 一根长度8000的线段上染色,求染完之后,每个颜色在线段上有多少个间断的区间. 思路: 区间问题用线段树,成段的更新区间,最后把所有的区间下压到叶子结点,统计叶子结点的颜色. #include <iostream> #include <cstring> #include <cstdio> using namespace std; int lz[32000],_hash[10000],color[10000],cnt; void push_down(in