【UKIEPC2015 H】【凸包思维栈操作】Sunlight 每座建筑的日照时间

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=2e5+10,M=0,Z=1e9+7,ms63=1061109567;
const double PI=acos(-1.0);
int casenum,casei;
int n;
int x[N],h[N];
int s[N],top;
double ans[N];
double angle(int p1,int p2)
{
	return atan(fabs(1.0*(h[p1]-h[p2])/(x[p1]-x[p2])));
}
int main()
{
	while(~scanf("%d",&n))
	{
		for(int i=1;i<=n;i++)scanf("%d%d",&x[i],&h[i]);
		top=0;
		for(int i=1;i<=n;i++)
		{
			while(top&&h[s[top]]<=h[i])--top;
			while(top>1&&angle(i,s[top-1])>angle(i,s[top]))--top;
			ans[i]=top?angle(i,s[top]):0;
			s[++top]=i;
		}
		top=0;
		for(int i=n;i>=1;i--)
		{
			while(top&&h[s[top]]<=h[i])--top;
			while(top>1&&angle(i,s[top-1])>angle(i,s[top]))--top;
			ans[i]+=top?angle(i,s[top]):0;
			s[++top]=i;
		}
		for(int i=1;i<=n;i++)printf("%.16f\n",(1-ans[i]/PI)*12);
	}
	return 0;
}
/*
【trick&&吐槽】
凸包思维是很常见的哦

【题意】
有n(2e5)座建筑物,我们认为每个建筑物的宽度都为0。
现在从西向东,告诉你每个建筑物的坐标和高度。
让你对于每个建筑物,输出其顶部被阳光照射的时间。

【类型】
栈

【分析】
这道题和我们的生活很有关联2333。
如果无遮挡,日照时间最大为12小时,
否则遮挡可能来自于西面,也可能来自于东面。
我们把两侧的遮挡时间算一下,用12减一下。
或者一侧的光照时间按照最大6小时来算,两侧加起来即可。

然而固定方向后,我们该如何求遮挡时间呢?
我们作图或者结合生活,发现我们,就算使得太阳从0°转到90°。
其实对一个楼的光照产生遮挡的楼层也只会是另外的唯一一个。
这对应的其实是最大遮挡角。

至于它的求法,我们可以暴力枚举在它之前的所有楼,算出一个最大值。
然而这个复杂度是O(n^2),我们就要爆炸啦。
那么用什么方法求比较好呢?

没错,就是用栈时限
这个其实类似于凸包的形式。
我们可以用栈的方式处理这个关系。
如果它的高度比栈顶的高,那么我们就一直退栈。
如果它的高度没栈顶的高,那么栈顶的都可能对其产生遮挡。
我们查看栈最上方的两个建筑物对它的遮挡角的大小,
1,如果栈顶的遮挡角<=次栈顶的遮挡角,那么我们肯定退栈。
因为当前栈顶元素对于遮挡而言永远没有次栈顶的优。
2,如果栈顶的遮挡角>次栈顶的遮挡角。那么当前栈顶元素就是所有点中,对当前点的遮挡性最强的。
因为栈中的点,连起来其实会形成一个恰好的凸包,所以这个结论很明显。

于是我们两个方向都扫描一下,就能得到答案。

【时间复杂度&&优化】
O(n)

【数据】
input
4
1 1
2 2
3 2
4 1
output
9.0000
12
12.00000
9.0

input
5
100 50
125 75
150 100
175 125
200 25
output
9.0
9.0
9.0
12.0
6.9357496
*/

你可能感兴趣的:(算法,栈,ACM,ICPC,凸包)