【BZOJ1818】[Cqoi2010]内部白点 扫描线+树状数组

【BZOJ1818】[Cqoi2010]内部白点

Description

无限大正方形网格里有n个黑色的顶点,所有其他顶点都是白色的(网格的顶点即坐标为整数的点,又称整点)。每秒钟,所有内部白点同时变黑,直到不存在内部白点为止。你的任务是统计最后网格中的黑点个数。 内部白点的定义:一个白色的整点P(x,y)是内部白点当且仅当P在水平线的左边和右边各至少有一个黑点(即存在x1 < x < x2使得(x1,y)和(x2,y)都是黑点),且在竖直线的上边和下边各至少有一个黑点(即存在y1 < y < y2使得(x,y1)和(x,y2)都是黑点)。

Input

输入第一行包含一个整数n,即初始黑点个数。以下n行每行包含两个整数(x,y),即一个黑点的坐标。没有两个黑点的坐标相同,坐标的绝对值均不超过109。

Output

输出仅一行,包含黑点的最终数目。如果变色过程永不终止,输出-1。

Sample Input

4
0 2
2 0
-2 0
0 -2

Sample Output

5
数据范围
36%的数据满足:n < = 500
64%的数据满足:n < = 30000
100%的数据满足:n < = 100000

题解:容易发现,对于x坐标相同的点,最有用的点一定是y坐标最小和最大的点;y相同的,最有用的就是x坐标最小和最大的点。并且新加入的内部白点的x和y坐标一定不会比原来更优,所以。。。变色过程只持续了最多1秒。

于是我们先离散化,求出x坐标相同时y的最小值和最大值以及y相同时x的最小最大值,然后可以看成给你一堆线段求这些线段的交点个数。用扫描线+树状数组即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn=100010;
typedef long long ll;
int n,mx,my;
ll ans;
int xl[maxn],xr[maxn],yl[maxn],yr[maxn],s[maxn];
struct point
{
	int x,y;
}p[maxn];
struct node
{
	int val,org;
}num[maxn];
vector<int> q1[maxn],q2[maxn];
vector<int>::iterator it;
bool cmpv(const node &a,const node &b)
{
	return a.val<b.val;
}
bool cmpx(const point &a,const point &b)
{
	return a.x<b.x;
}
inline void updata(int x,int val)
{
	for(int i=x;i<=mx;i+=i&-i)	s[i]+=val;
}
inline int query(int x)
{
	int i,ret=0;
	for(i=x;i;i-=i&-i)	ret+=s[i];
	return ret;
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
int main()
{
	n=rd();
	int i;
	for(i=1;i<=n;i++)	num[i].val=rd(),p[i].y=rd(),num[i].org=i;
	sort(num+1,num+n+1,cmpv);
	for(i=1;i<=n;i++)
	{
		if(i==1||num[i].val>num[i-1].val)	mx++;
		p[num[i].org].x=mx;
	}
	for(i=1;i<=n;i++)	num[i].val=p[i].y,num[i].org=i;
	sort(num+1,num+n+1,cmpv);
	for(i=1;i<=n;i++)
	{
		if(i==1||num[i].val>num[i-1].val)	my++;
		p[num[i].org].y=my;
	}
	memset(xl,0x3f,sizeof(xl)),memset(yl,0x3f,sizeof(yl));
	for(i=1;i<=n;i++)
	{
		xr[p[i].x]=max(xr[p[i].x],p[i].y),xl[p[i].x]=min(xl[p[i].x],p[i].y);
		yr[p[i].y]=max(yr[p[i].y],p[i].x),yl[p[i].y]=min(yl[p[i].y],p[i].x);
	}
	for(i=1;i<=mx;i++)	q1[xl[i]].push_back(i),q2[xr[i]].push_back(i);
	for(i=1;i<=my;i++)
	{
		for(it=q1[i].begin();it!=q1[i].end();it++)	updata(*it,1);
		ans+=query(yr[i])-query(yl[i]-1);
		for(it=q2[i].begin();it!=q2[i].end();it++)	updata(*it,-1);
	}
	printf("%lld",ans);
	return 0;
}
时间: 2024-10-10 20:30:20

【BZOJ1818】[Cqoi2010]内部白点 扫描线+树状数组的相关文章

bzoj 1818 [CQOI 2010] 内部白点 - 扫描线 - 树状数组

题目传送门 快速的列车 慢速的列车 题目大意 一个无限大的方格图内有$n$个黑点.问有多少个位置上下左右至少有一个黑点或本来是黑点. 扫描线是显然的. 考虑一下横着的线段,取它两个端点,横坐标小的地方放一个+1,大的地方放一个-1事件. 然后扫描,扫到的横着的线段更新,竖着的线段用树状数组求答案. 然后考虑这一列上原来存在的黑点有没有被统计,如果没有就加上. Code 1 /** 2 * bzoj 3 * Problem#1818 4 * Accepted 5 * Time: 1824ms 6

BZOJ 1818 内部白点(离散化+树状数组)

此题就是1227 的弱化版. 画个图或者稍微证明一下就能够知道,一定不会超过一次变换. 那么我们只需要统计有多少个白点会变黑,换句话说就是有多少个白点上下左右都有黑点. 离散化横坐标,因为没有黑点在的列是没有任何意义的,对答案也没有贡献. 然后处理每一行,对于每一行,维护一个BIT也就是哪些点会产生贡献,这个BIT最多只会有n次修改,n次查询. 所以时间复杂度为O(nlogn). # include <cstdio> # include <cstring> # include &l

[扫描线+树状数组]解决矩形包含点的问题

今天做到第二题,大部分的思路都理解了之后最后剩下一个问题 zzx:“然后扫描线+树状数组搞一下就好了” 看到这两个算法就产生了一种我肯定会的错觉... 然后后来发现并不会的时候很惭愧... 然后十分感谢YDC,在之前完全陌生的情况下 我去问这样一个问题 超级超级超级耐心地给我解答  我问着每一个他们看起来显然的具体处理方法 突然发现真的像张老师说的:“你普及升提高的这条路没有怎么走,中间那块知识是空着的啊” 今天才切实体会到...但是或许正是这样今天学到这样的一个东西更让我觉得开心吧 树状数组解

【BZOJ4009】[HNOI2015]接水果 DFS序+整体二分+扫描线+树状数组

[BZOJ4009][HNOI2015]接水果 Description 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black,  她觉得这个游戏太简单了,于是发明了一个更加难的版本.首先有一个地图,是一棵由 n 个顶点.n-1 条边组成的树(例如图 1给出的树包含 8 个顶点.7 条边).这颗树上有 P 个盘子,每个盘子实际上是一条路径(例如图 1 中顶点 6 到顶点 8 的路径),并且每个盘子还有一个权值.第 i 个盘子

FZU 2225 小茗的魔法阵 扫描线+树状数组

这个题和一个CF上的找"Z"的题差不多,都是扫描线+树状数组 从右上角的主对角线开始扫描,一直扫到左下角,每次更新,右延伸等于该扫描线的点,注意在其所在的树状数组更新就好了 时间复杂度O(n^2logn) #include <stdio.h> #include <iostream> #include <vector> #include <math.h> #include <set> #include <map> #

【bzoj4540】[Hnoi2016]序列 单调栈+离线+扫描线+树状数组区间修改

题目描述 给出一个序列,多次询问一个区间的所有子区间最小值之和. 输入 输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数.接下来一行,包含n个整数,以空格隔开,第i个整数为ai,即序列第i个元素的值.接下来q行,每行包含两个整数l和r,代表一次询问. 输出 对于每次询问,输出一行,代表询问的答案. 样例输入 5 5 5 2 4 1 3 1 5 1 3 2 4 3 5 2 5 样例输出 28 17 11 11 17 题解 单调栈+离线+扫描线+树状数组区间修改 首先把使用单调栈找出每个

HDU - 5741 Helter Skelter 扫描线 + 树状数组

HDU - 5741 我们枚举段的起点和终点, 那么每一种情况0的范围是[lx, rx], 1的出现范围是[ly, ry], 可以在二维平面上用矩形表示. 然后问题就变成了询问点有没有被至少一个矩形覆盖, 扫描线 + 树状数组就可以了. #pragma GCC optimize(2) #pragma GCC optimize(3) #include<bits/stdc++.h> #define LL long long #define LD long double #define ull un

BZOJ1818: [Cqoi2010]内部白点

1818: [Cqoi2010]内部白点 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1004  Solved: 485[Submit][Status][Discuss] Description 无限大正方形网格里有n个黑色的顶点,所有其他顶点都是白色的(网格的顶点即坐标为整数的点,又称整点).每秒钟,所有内部白点同时变黑,直到不存在内部白点为止.你的任务是统计最后网格中的黑点个数. 内部白点的定义:一个白色的整点P(x,y)是内部白点当且仅当P

HDU 5862 Counting Intersections (离散化+扫描线+树状数组)

题意:给你若干个平行于坐标轴的,长度大于0的线段,且任意两个线段没有公共点,不会重合覆盖.问有多少个交点. 析:题意很明确,可是并不好做,可以先把平行与x轴和y轴的分开,然后把平行y轴的按y坐标从小到大进行排序,然后我们可以枚举每一个平行x轴的线段, 我们可以把平行于x轴的线段当做扫描线,只不过有了一个范围,每次要高效的求出相交的线段数目,可以用一个优先队列来维护平行y轴的线段的上坐标, 如果在该平行于x轴的范围就给相应的横坐标加1,这样就很容易想到是用树状数组来维护,然后每次求出最左边和最右边