• COCI2013-2014 Contest#3 F 单调栈


    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)的所有点对(为了防止下面计算左端点时出现问题)

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

    [ ]

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

    #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
    ",ans);
    }
    
    
  • 相关阅读:
    JavaScript简单的日历
    CSS动画实现滚动播放
    视频弹窗自动播放 关闭停止
    简单通过js实现轮播图
    switch case的应用
    显示、隐藏
    .container的应用
    用CSS对HTML进行初始化设置
    CSS Id 和 Class选择器
    给文字添加阴影效果
  • 原文地址:https://www.cnblogs.com/chasedeath/p/13610920.html
Copyright © 2020-2023  润新知