首页 > 其他分享 >线段树与离散化技巧 Mayor's posters——poj 2528

线段树与离散化技巧 Mayor's posters——poj 2528

时间:2024-09-13 20:45:58浏览次数:1  
标签:海报 int 2528 num poj 区间 posters lazy id

问题描述:
有一堵海报墙,从左到右一共有10000000个小块,墙上贴了许多海报,每张海报的高度与墙的高度相同,宽度不同,新帖的海报会将原有的海报覆盖,问当所有人把海报贴完是,墙上可以看到几张海报
输入:
第一行输入一个整数c表示测试数,每个测试第一行输入一个整数n(1<=N<=10000),代表张贴海报数量,之后的n行,每行输入两个整数,L,R,表示海报的左右位置,它覆盖的区间为L,R
输出:
对于每个测试,输出能看到的海报数量

题目分析:
如何才能判断一面墙上有几张海报,正常我们用肉眼判断肯定是找每一张的特征,先把不同的海报区分开来,然后一个一个数,如果在计算机中,那每张海报在记录时肯定要有标记,按照题目意思,该标记还要可以被部分覆盖,那么就简单了,我们可以将一张海报看成一个区间,在这个区间中只有一种标记代表这张海报,这种标记填满这个区间,那么就可以被部分覆盖,我们把每张海报编号,在及记录(张贴)该海报时把该编号写满该区间每个元素,(我们这里是指数组的值),那么之后记录的海报就可以对区间重新赋值,这些似乎都是区间操作,那么线段树肯定是首选,可以使用tag标记结合哈希表检索这个海报是否被覆盖,但该墙上有10000000个小块,就意味着我们需要创建一个N=10000000大小的数组,而线段树tree[4N],这个空间是巨大的,有没更省空间的方法,答案是有的,我们观察到,其实海报原有的宽度并不影响我们计算,完全可以改成任何一个合理的数,有这种特性的例子可以利用离散化的技巧,总共10000张海报那么最多就有20000个left和right,N=20000直接省下极大空间,但需要注意的是,离散化会将原本离散的数据变得紧凑,这是离散化的用途,但在本题中需要更改,比方说一个大区间L1,R1,这个大区间中只有两个小区间[L1,r],[l,R1],那么离散化之后r和l会被看作是连续的,会导致[r,l]的大区间部分被覆盖,实际上并没有, 那么最终结果只有两张海报,大区间[L1,R1],被误判为完全覆盖了,要避免这个问题只需要在离散化是在r和l之间在添加一个元素即可,下面直接看代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 100014;
int n, m;
int lazy[MAXN << 2];
int num[MAXN << 2];
int left1[MAXN];
int right1[MAXN];
bool vis[MAXN];
int ans = 0;

int ls(int id) { return id << 1; }
int rs(int id) { return id << 1 | 1; }

//本题并没有定义线段树区间代表什么,所以build函数并没有实际意义
void build(int id, int l, int r) {
	if (l == r) {
		return;
	}
	int mid = (l + r) >> 1;
	build(ls(id), l, mid);
	build(rs(id), mid + 1, r);
}

void pushdown(int id, int l, int r) {
	if (lazy[id] == 0) {
        //标记为0代表id区间并没有张贴海报,当然只是说该区间没有,要知道线段树中有很多区间,大区间中也有很多小区间,这点需要明确,并不是线性表,并不是线性表,并不是线性表,重要的事情说三遍
		return ;
	}
    //将该标记向下传递给跟小区间,相当于再次张贴海报将原有海报覆盖
	lazy[ls(id)] = lazy[id];
	lazy[rs(id)] = lazy[id];
    //删除标记
	lazy[id] = 0;
}

void update(int id, int l, int r, int p, int q, int v) {
	if (l >= p && r <= q) {
		lazy[id] = v;  //v就是上文说到的编号
		return;
	}
	pushdown(id, l, r);  //向下覆盖
	int mid = (l + r) >> 1;
    //直到将张贴的海报的区间所覆盖的所有节点区间覆盖
	if (p <= mid) update(ls(p), l, mid, p, q, v);
	if (q > mid) update(rs(p), mid + 1, r, p, q, v);
}

void query(int id, int l, int r) {
	if (lazy[id] && !vis[lazy[id]]) {
		vis[lazy[id]] = 1;  //这个数组一开始全是零
		ans++;
		return;
	}
    //注意这个判断为什么写在这里
	if (l == r) {
		return;
	}
	pushdown(id, l, r);
	int mid = l + r;
	query(ls(id), l, mid);
	query(rs(id), mid + 1, r);
}

int main() {
	int t;
	cin >> t;
	while (t--) {
		ans = 0;
		memset(lazy, 0, sizeof(lazy));
		memset(vis, 0, sizeof(vis));
		cin >> n;
		int cnt = 1;
		for (int i = 1; i <= n; i++) {
			int a, b;
			cin >> left1[i] >> right1[i];
			num[cnt++] = left1[i];
			num[cnt++] = right1[i];
		}
        //先排序一次,以便第一次去重
		sort(num + 1, num + cnt);
		m = unique(num + 1, num + cnt) - (num + 1);
		int an = m;
		for (int i = 2; i <= m; i++) {
			if (num[i] - num[i - 1] > 1) {
				num[++an] = num[i - 1] + 1;  //加入中间元素,避免上文提到的离散化出现的错误
			}
		}
        //重新排序后,加入的中间元素回自动被插入到相应位置
		sort(num + 1, num + an + 1);
		build(1, 1, an);
		for (int i = 1; i <= n; i++) {
			int p = lower_bound(num + 1, num + an + 1, left1[i]) - num;
			int q = lower_bound(num + 1, num + an + 1, right1[i]) - num;
			update(1, 1, an, p, q, i);
		}
		query(1, 1, an);
		cout << ans << endl;
	}
	return 0;
}

标签:海报,int,2528,num,poj,区间,posters,lazy,id
From: https://www.cnblogs.com/oQAQo/p/18409808

相关文章

  • 高级java每日一道面试题-2024年9月06日-基础篇-Java中的PO、VO、BO、DO、DAO、DTO、PO
    如果有遗漏,评论区告诉我进行补充面试官:Java中的PO、VO、BO、DO、DAO、DTO、POJO是什么意思?我回答:PO持久化对象(PersistentObject)PO是持久化对象,用于表示数据库中的实体或表的映射通常与数据库表的结构和字段对应PO的属性对应数据库表的字段,可以进行持久化操作(新......
  • POJ - 3368
    还是rmq.原来如此代码:#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+7;inta[N],b[N],rmq[N][20];intpw(intk){intres=1;while(k--)res*=2;returnres;}intgetk(intl,intr){intk=0;while(r-l+1>=pw(k+1))++k;......
  • POJ-3264
    这是rmq半懂不懂(因为已经会线段树了)但是!它的代码真的好短啊啊啊啊啊!#include<bits/stdc++.h>usingnamespacestd;intdp1[500010][20],dp2[500010][20],w[1000010];intmain(){ intn,k,q,l,r; ios::sync_with_stdio(0); cin.tie(0); cin>>n>>q; for(inti=1;i<=n;i+......
  • POJ - 3150
    题解题意:题面很臭很长。大意是,有一个大小为N的环,给出M,K,D,以及N个数。我们进行K次操作,每次操作把距离当前点不超过D的累加到当前点,结果模M。思路:因为要进行K次,每次的原则是一样的,我们可以想到用矩阵来优化,如果i能到达j,把么base[i][j]=1;则结果ans=A(base^K)。但是需要优化,时间复杂......
  • POJ - 3150
    题解题意:题面很臭很长。大意是,有一个大小为N的环,给出M,K,D,以及N个数。我们进行K次操作,每次操作把距离当前点不超过D的累加到当前点,结果模M。思路:因为要进行K次,每次的原则是一样的,我们可以想到用矩阵来优化,如果i能到达j,把么base[i][j]=1;则结果ans=A(base^K)。但是需要优化,时间复杂......
  • POJ - 3296
    对于操作来说,第一次是最重要的,后来每次倒入水量是相同的。这是因为后面的总液体量不变的情况下,ans=第一次后液体浓度*后几次液体浓度的积所以由1/v^2<1/v^2-x^2(v,x>0),易得后几次水量相同那么,对于第一次来说可以用三分法来求极值。代码:#include<bits/stdc++.h>usingn......
  • POJ-1066
    题解告诉我:大意:在一个矩形区域内,有n条线段,线段的端点是在矩形边上的,有一个特殊点,问从这个点到矩形边的最少经过的线段条数最少的书目,穿越只能在中点穿越思路:需要巧妙的转换一下这个问题,因为从一个点到终点不可能“绕过”围墙,只能穿过去,所以门是否开在中点是无所谓的,只要求四周线......
  • POJ - 3318
    他说:O(n^3)是过不了滴一搜……6代码和题解没有实质区别:#include<cstdio>#include<ctime>#include<cstdlib>usingnamespacestd;inta[505][505],b[505][505],c[505][505];intmain(){ srand(time(NULL)); intn; ios::sync_with_stdio(0); cin.tie(0); while(cin......
  • POJ - 1765
    第一次做扫描线挺好的#include<cstdio>#include<cstring>#include<algorithm>usingnamespacestd;structppx{ intl,r; doubleleft,right; doublelen; intcover;//记录重边情况}tree[4*200+10];doubley[200+10];//对应的序号装对应的边structpp{ doublex......
  • POJ - 1870
    先把蜂巢快递柜画出来:__________/\__/\__/\__/\____/\__/\__/53\__/\__/\__/\__/\__/52\__/54\__/\__/\\__/\__/51\__/31\__/55\__/\__//\__/50\__/30\__/32\__/56\__/\\__/49\__/29\__......