Description
Input
Output
Sample Input
输入1:
3 10
1 2 6
0 4 3
0 8 2
输入2:
5 15
0 4 3
1 5 5
1 6 6
0 9 2
0 10 3
Sample Output
输出1:
6.000000
输出2:
8.500000
Data Constraint
30%的数据,N<=1000
Hint
分析
对于一个区间x,y,我们对于除了x,y以外的发射器忽略,考虑x-y之间的。
假设有若干个发射器,每个能覆盖区间z-y,那么我们可以找到一个最小的z,记为z1。
同理,假设这若干个发射器能覆盖区间x-z,同样可以找到一个最大的z,记为z2.
那么x,y这个区间对于答案的贡献就是y-x-max(0,z1-z2).
现在问题就是如何求z1,z2。很明显我们可以发现,其实z2就是z1倒过来求一波就可以了,所以我们只讨论如何求解z1.
我们首先从左到右扫一遍,用个数据结构(stl速度起飞)维护一下出现过的发射器,接下来我们具体讨论怎么维护。
首先定义一种东西叫做配对点,意思就是,对于每个发射器,他不能覆盖范围的最左(比如样例中第一个发射器的配对点就是2.5)
注意在扫之前先按照x升序排序。
在扫的时候维护一个三元组:x,h,t分别表示发射器的横坐标,高度和配对点。
接下来我们只讨论两个发射器A,B之间的情况(保证A的横坐标小于B的)
1.假如H(A)<=H(B),弹出B,因为A比B优。
2.否则,如果T(A)<
T(B),同样也能弹出B,理由如上。
然后注意维护的时候,要保证数据结构中的发射点,高度按照降序,配对点反而是升序(这个其实是个性质,不用单独维护)。
一开始如果建筑不是发射点,就把前两个发射点先加入数据结构中。。
在从左到右的过程中,一边走一边维护最新加入点的配对点(前面加入过的不用维护,因为不会改变),如果有T(A)>T(B),那么踢出B,(A为次新点,B为最新点),一直踢出,直到不能符合上式。。
这样数据结构中最后剩下的点就是能够做出贡献的点辣!
同样的方法,反着做一遍,求出z2,最后直接求解就好了。
时间复杂度经过证明(证明懒得写了,自己看英文题解吧)是O(n)。
代码
#include <bits/stdc++.h>
#define N 500005
int n,l;
int t[N],x[N],h[N];
struct NOTE
{
NOTE(int a):x(a),y(0){}
NOTE(int a,int b):x(a),y(b){}
int x,y;
};
double ansl[N],ansr[N];
double getPos(const NOTE & now)
{
int &x1 = x[now.x];
int &y1 = h[now.x];
int &x2 = x[now.y];
int &y2 = h[now.y];
return ((double)x1 * (y1 - y2) - (double)y1 * (x1 - x2)) / (y1 - y2);
}
void add(NOTE &now,int c)
{
if (getPos(now) < getPos(NOTE(now.x,c)))
now.y = c;
}
void getAnsPos(double *ans)
{
std::vector<NOTE> V;
for (int i = 0; i < n; i++)
{
for (;V.size() && h[V.back().x] <= h[i];)
{
V.pop_back();
}
if (V.size())
{
for (add(V.back(),i); V.size() > 1; V.pop_back())
{
add(V[V.size() - 2],i);
if (getPos(V.back()) < getPos(V[V.size() - 2]))
break;
}
}
if (t[i])
{
V.push_back(i);
ans[i] = x[i];
}
else
if (V.size())
ans[i] = std::min(getPos(V.back()),(double)x[i + 1]);
else ans[i] = x[i + 1];
}
}
int main()
{
freopen("odasiljaci.in","r",stdin);
freopen("odasiljaci.out","w",stdout);
scanf("%d%d",&n,&l);
n++;
for (int i = 1; i < n; i++)
scanf("%d%d%d",&t[i],&x[i],&h[i]);
x[n] = l;
n++;
getAnsPos(ansr);
std::reverse(t,t + n);
std::reverse(x,x + n);
std::reverse(h,h + n);
for (int i = 0; i < n; i++)
x[i] = l - x[i];
getAnsPos(ansl);
std::reverse(ansl,ansl + n);
for (int i = 0; i < n; i++)
ansl[i] = l - ansl[i];
double ans = 0;
for (int i = 0; i < n - 1; i++)
if (ansr[i] > ansl[i + 1])
ans += ansr[i] - ansl[i + 1];
printf("%.6lf\n",l - ans);
}