【poj2528】Mayor's posters

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 59254   Accepted: 17167

Description

The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral posters at all places at their whim. The city council has finally decided to build an electoral wall for placing the posters and introduce the following rules: 
  • Every candidate can place exactly one poster on the wall. 
  • All posters are of the same height equal to the height of the wall; the width of a poster can be any integer number of bytes (byte is the unit of length in Bytetown). 
  • The wall is divided into segments and the width of each segment is one byte. 
  • Each poster must completely cover a contiguous number of wall segments.

They have built a wall 10000000 bytes long (such that there is enough place for all candidates). When the electoral campaign was restarted, the candidates were placing their posters on the wall and their posters differed widely in width. Moreover, the candidates started placing their posters on wall segments already occupied by other posters. Everyone in Bytetown was curious whose posters will be visible (entirely or in part) on the last day before elections. 
Your task is to find the number of visible posters when all the posters are placed given the information about posters' size, their place and order of placement on the electoral wall. 

Input

The first line of input contains a number c giving the number of cases that follow. The first line of data for a single case contains number 1 <= n <= 10000. The subsequent n lines describe the posters in the order in which they were placed. The i-th line among the n lines contains two integer numbers li and ri which are the number of the wall segment occupied by the left end and the right end of the i-th poster, respectively. We know that for each 1 <= i <= n, 1 <= li <= ri <= 10000000. After the i-th poster is placed, it entirely covers all wall segments numbered li, li+1 ,... , ri.

Output

For each input data set print the number of visible posters after all the posters are placed. 

The picture below illustrates the case of the sample input. 

Sample Input

1
5
1 4
2 6
8 10
3 4
7 10

Sample Output

4

Source


【题解】

这题不要看题目描述。直接看图片和样例输入就能知道是什么意思了。

最后问最上面有多少种不同的海报??

然后1000W的int数组38MB.为了实现线段树。必然要开个4倍左右。

然后内存限制为64MB.会超!

为了解决内存问题。要用到离散化的知识。

比如样例输入的区间

1 4
2 6
8 10
3 4
7 10
可以得到端点1,2,3,4,6,7,8,10; (相同则只取一个就好,即剔除重复的)

当然这里省略了将端点升序排这个步骤。

然后我们给每个端点标一个序号

其实就是排序后的数组下标

即a[1]=1,a[2] = 2,a[3] = 3,a[4] = 4,a[5] = 6...以此类推。

a[]  =1 2 3 4 6 7 8 10

key =1 2 3 4 5 6 7 8

然后我们就可以把原来的区间写成

[1,4];

[2,5];

[7,8];

[3,4];

[6,8];

最后同样可以得到相同的结果。

而我们已经成功地把所给的巨大的区间端点和1..2*n对应起来了;

之后我们对在进行过离散化的区间进行覆盖就可以了。

覆盖的方法就是线段树了。

但是还有一个问题。就是离散化有一种情况会发生错解。

如下:

1,10

1,4

6,10

我们排序后得到

a[]  = 1 4 6 10

key= 1 2 3 4

则区间变为

[1,4]

[1,2]

[3,4]

看起来1,4已经被后面两个全部覆盖了。

但实际上

在未离散之前的坐标基础上

1,10还有一个点[5,5]未被覆盖。

如何解决呢?

其实就是因为4,6不是连续的。不能按照顺序他们标一个x和x+1;

那么我们可以在4和6之间加一个5;就变成了

a[]= 1 4 5 6 10

key=1 2 3 4 5 

则原区间变成

[1,5]

[1,2]

[4,5]

这样我们就能知道还有一个点未被覆盖。

因为我们不知道会不会有这种情况。

所以

我们在将端点进行去重且排序过后。

如果a[i]>a[i-1]+1,则插入一个元素a[i-1]+1在a[i]和a[i-1]之间。i∈[2,x]x是key的最大值。

然后l[i],r[i]的检索key值的工作则可以用lower_bound函数来完成。

(二分查找并不好写,但是如果已经确定在序列中的应该会比较容易。所以手写也可以啦)

之后。进行线段树的操作。

设cover[i]表示i这个节点所在的区间覆盖了某种颜色。

如果寻找到的节点所在的区间被要求的区间所覆盖。

则将这个节点所代表的区间染上相应的颜色。

这个颜色可以作为lazy_tag来使用。之后一旦遇到一个这样的lazy_tag,就把儿子节点染成这个lazy_tag的标志。

然后父亲节点lazy_tag的标志改为-1,表示这个区间下面可能还有其他不同的颜色。不能一概而论了。

然后在询问的时候。如果遇到了那些能够"一概而论"的区间,就判断这个颜色是否出现过。如果没有出现过就递增答案。

最后输出答案就好。

特别注意;

一组数据,color啊,颜色的判重数组啊什么的都要重置。千万不要写在外面!!

【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>

int T,n,a1[50000],l[20001],r[20001],color[200000],ans;
bool bo[20001];

void push_down(int rt)
{
	if (color[rt] != -1) //如果之前有被染过色
	{
		color[rt << 1] = color[rt << 1 | 1] = color[rt];//把儿子节点的颜色改成这个父亲节点的颜色。
		color[rt] = -1;//之后就不能一概而论了,因为接下来可能修改了这个节点所代表的区间内的一部分区间的颜色;
	}
}

void updata(int l, int r, int num,int begin, int end, int rt) //当前节点rt所代表的区间为[begin,end],然后所要染色的区间为[l,r]
{//要染成的颜色为num
	if (l <= begin && end <= r)//如果当前节点所代表的区间在所要染色的区间内。
	{
		color[rt] = num;//则直接染色(因为在区间内,所以可以给这个区间"一概而论"地染成相同颜色)
		return;
	}
	push_down(rt);//判断一下这个节点之前是否被染过色。
	int m = (begin + end) >> 1;//取中点。
	if (l <= m)
		updata(l, r, num, begin, m, rt << 1);//如果这个节点所代表的区间的左半部分和所要染色的区间有交集。则往左不断逼近这个区间
	if (m < r)
		updata(l, r, num, m + 1, end, rt << 1 | 1);//如果右边有交集则同理。
}

void query(int l, int r, int rt)//询问l,r这个区间内的颜色数目
{
	if (color[rt] != -1)//如果这个节点所代表的区间内的颜色是相同的。
	{
		if (!bo[color[rt]])//如果之前没有计数过这个颜色
		{
			bo[color[rt]] = true;//标记为已经记录过
			ans++;//递增答案。
		}
		return;
	}
	if (l == r)//如果左端点和右端点重合则结束。
		return;
	int m = (l + r) >> 1;//取中点
	query(l, m, rt << 1);//往左儿子递增颜色
	query(m + 1, r, rt << 1 | 1);//往右儿子递增颜色。
}

int main()
{
	scanf("%d", &T);//输入数据的组数
	while (T--)//输入T组数据
	{
		memset(color, 255, sizeof(color));
		a1[0] = 0;
		scanf("%d", &n);//输入n个区间。
		for (int i = 1; i <= n; i++)
		{
			scanf("%d%d", &l[i], &r[i]);
			a1[0]++;//递增找到的端点个数
			a1[a1[0]] = l[i];//把这个端点记录下来。
			a1[0]++;
			a1[a1[0]] = r[i];
		}
		std::sort(a1 + 1, a1 + 1 + 2 * n);//然后用sort函数讲其升序排。
		a1[0] = 1;//然后要进行去重操作
		for (int i = 2; i <= 2*n; i++)//这里的i一定是大于等于a1[0]的。所以不会对数据造成影响。
			if (a1[i] != a1[i - 1])//直接判断是不是和前一个相等
			{
				a1[0]++;
				a1[a1[0]] = a1[i];//不相等则加入。
			}
		int end = a1[0];
		for (int i =2;i <= end;i++)//如果有前后不是连续的端点。则在这两个端点之间插入一个端点。
			if (a1[i] > a1[i - 1] + 1)
			{
				a1[0]++;
				a1[a1[0]] = a1[i - 1] + 1;
			}
		std::sort(a1 + 1, a1 + 1 + a1[0]);//说是插入操作,但实际上是加在数组的后面,所以排序一下。这样就等价于插入了
		for (int i = 1; i <= n; i++)
		{
			l[i] = std::lower_bound(a1 + 1, a1 + 1 + a1[0], l[i]) - a1; //然后检索每个端点它的key值。
			r[i] = std::lower_bound(a1 + 1, a1 + 1 + a1[0], r[i]) - a1;
			updata(l[i], r[i],i, 1, a1[0], 1);//以这个key值作为新的区间,然后做线段树操作。即染色
		}
		ans = 0;
		memset(bo, false, sizeof(bo));
		query(1, a1[0], 1);//统计1..a[0]区间内的颜色种类;
		printf("%d
", ans);//输出答案。
	}
	return 0;
}




原文地址:https://www.cnblogs.com/AWCXV/p/7632272.html