COCI2013-2014 Contest#3 F 单调栈

考虑每个小区间$[x_i,x_{i+1}]$中,外部光照进来只能是这样

Light1.png

其中两边是光源,红色表示被照到了

那么所有合法的被照到的段可以表示为$[x_i,x_{i+1}]$中的两个小段,即

1.左边照进来的$[s0_i,x_{i+1}]$

2.右边照进来的$[x_i,s1_i]$

考虑从左到右维护可能照到右边的较优光源

显然对于每一个光源都有一个当前能遮住它最多的城市,构成(光源,限制城市)这样的点对

对于每一个较优的这样的点对 可以维护一个单调栈,维护出来应该是这样的

Light2.png

有几个较为显然的性质:

1.所有点对的$h_i$递减,否则要么是上一个光源被完全遮住了,要么是这个点可以遮住上一个光源更多

2.靠右的点对能够覆盖的范围较大,即可行区间的左端点较小

否则它此时不会产生贡献,而当后面较高的点进来时,相对更矮的它也更容易被遮住

实际上,这样的形状就会构成一个类似上凸包的东西,但是凸包上只有一部分点产生贡献

考虑依次加入点$(x_i,h_i)$,显然可以弹掉$h_j<h_i$的所有点对(为了防止下面计算左端点时出现问题)

接下来的情况,类似维护凸包,但是要考虑更新限制城市 或者 弹掉点对

对于向左的情况,反着求一遍即可,最后可以减去两种区间都覆盖不到的部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include<bits/stdc++.h>
using namespace std;
typedef double db;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

char IO;
template <class T=int> T rd(){
T s=0; int f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}

const int N=3e5+10;

int n,D;
struct City{ int k,x,h; } C[N];

struct Lightpair{
//描述一个灯塔和右边遮住它最多的城市
int x,y;
Lightpair(const int &_x=0,const int &_y=0) : x(_x),y(_y) { }
db Left(){
return C[y].x+1.0*(C[y].x-C[x].x)/(C[x].h-C[y].h)*C[y].h;
}
// z这个发光对能照到的范围为[Left(),+oo)
} stk[N];
int top;
void Cover(Lightpair &x,int y){ if(Lightpair(x.x,y).Left()>x.Left()) x.y=y; }
db s[2][N];
// 将所有可能的发光区间描述为 [C[i].x,s[k][i]]


int main(){
n=rd(),D=rd();
rep(i,1,n) C[i].k=rd(),C[i].x=rd(),C[i].h=rd();
C[n+1].x=D;
rep(k,0,1) {
rep(i,0,n) {
while(top && C[stk[top].x].h<=C[i].h) top--; // 完全被遮住的,先弹掉
if(top) {
Cover(stk[top],i);
while(top>1){
Cover(stk[top-1],i);
if(stk[top-1].Left()>stk[top].Left()) break;
top--;
}
}
if(C[i].k) stk[++top]=i,s[k][i]=C[i].x; // 一定覆盖C[i].x,C[i+1].x
else if(top) s[k][i]=min(stk[top].Left(),(db)C[i+1].x); // 尝试让当前最优的点对覆盖过来
else s[k][i]=C[i+1].x; // 无覆盖
}
if(!k) {
reverse(C+1,C+n+1),top=0;
rep(i,1,n) C[i].x=D-C[i].x;
} else {
reverse(s[k],s[k]+n+1);
rep(i,0,n) s[k][i]=D-s[k][i];
}
}
db ans=D; // 减去不合法的
rep(i,0,n) if(s[0][i]>s[1][i]) ans-=s[0][i]-s[1][i];
printf("%.3lf\n",ans);
}