zoukankan      html  css  js  c++  java
  • 【BZOJ1502】【NOI2005】月下柠檬树

    Portal

      
      传送门
      
      
      

    Solution

      
      显然的是,每一个圆的影子,就是从树上的圆按光线方向平移至地面的圆。至于两个圆之间的连接部分,则是每两个在树上相邻的圆的,对应的影子圆的,公切线围起来的部分,如下图所示
      

      
      所以我们现在要求每两个在原树上相邻的圆的影子圆构成的图形的并。只看(x)轴上半部分,可以把它想象成一个函数,求单点值是(O(n))的,我们不妨用辛普森积分来解决......
      
      相邻圆的公切线和x轴的夹角是可以求出来的,然后就能解出公切线的解析式,以及有效范围。注意这些东西要预处理!千万不要放在求值函数里面。(EPS)大约设置到(10^{-7})才不会出错,效率也相对比较高。
      
      
      

    Code

      

    #include <cstdio>
    #include <cmath>
    using namespace std;
    const int N=505;
    const double EPS=1e-7,INF=1e10,PI=3.14159265358979323846;
    int n,sum;
    double alpha,h[N],p[N],r[N];
    double k[N],b[N],lx[N],rx[N];
    bool exist[N];
    inline double max(double x,double y){return x>y?x:y;}
    inline double min(double x,double y){return x<y?x:y;}
    inline void swap(int &x,int &y){x^=y^=x^=y;}
    inline bool in(int a,int b){
    	if(r[a]>r[b]) swap(a,b);
    	return p[a]+r[a]-EPS<=p[b]+r[b]&&p[a]-r[a]+EPS>=p[b]-r[b];
    }
    double calc(int a,int b,double &k,double &bb,double &xl,double &xr){
    	if(p[a]>p[b]) swap(a,b);
    	double beta=asin((r[b]-r[a])/(p[b]-p[a]));
    	k=tan(beta);
    	double tx;
    	if(r[a]>=r[b]){
    		beta=-beta;
    		tx=p[b]+r[b]/sin(beta);	
    		bb=-k*tx;	
    		xr=tx-(cos(beta)*(r[b]/tan(beta)));
    		xl=tx-(cos(beta)*(r[a]/tan(beta)));
    	}
    	else{
    		tx=p[a]-r[a]/sin(beta);
    		bb=-k*tx;
    		xl=tx+(cos(beta)*(r[a]/tan(beta)));
    		xr=tx+(cos(beta)*(r[b]/tan(beta)));
    	}
    }
    double f(double x){
    	double res=0;
    	for(int i=1;i<=n;i++) 
    		if(fabs(x-p[i])<=r[i]) 
    			res=max(res,sqrt(r[i]*r[i]-fabs(x-p[i])*fabs(x-p[i])));
    	for(int i=1;i<n;i++)
    		if(exist[i])
    			if(lx[i]<=x+EPS&&x-EPS<=rx[i])
    				res=max(res,k[i]*x+b[i]);
    	return res;
    }
    double simpson(double l,double r){
    	double mid=(l+r)*0.5;
    	return (f(l)+4*f(mid)+f(r))*(r-l)/6;
    }
    double solve(double l,double r){
    	double mid=(l+r)/2,lmid=(l+mid)/2,rmid=(mid+r)/2;
    	double val=simpson(l,r);
    	if(fabs(val-(simpson(l,mid)+simpson(mid,r)))<EPS) return val;
    	return solve(l,mid)+solve(mid,r);
    }
    int main(){
    	scanf("%d%lf",&n,&alpha);
    	n++;
    	for(int i=1;i<=n;i++){
    		scanf("%lf",h+i);
    		h[i]+=h[i-1];
    		p[i]=h[i]/tan(alpha);
    	}
    	for(int i=1;i<n;i++) scanf("%lf",r+i);
    	double xl=INF,xr=-INF;
    	for(int i=1;i<=n;i++){
    		xl=min(xl,p[i]-r[i]);
    		xr=max(xr,p[i]+r[i]);
    	}
    	for(int i=1;i<n;i++){
    		exist[i]=!in(i,i+1);
    		if(exist[i])
    			calc(i,i+1,k[i],b[i],lx[i],rx[i]);
    	}
    	printf("%.2lf
    ",solve(xl,xr)*2);
    	return 0;
    }
    
  • 相关阅读:
    Thymeleaf学习记录(1)--启动模板及建立Demo
    Redis教程基本命令
    错误:Attempted to load applicationConfig: [classpath:/application.yml] but snakeyaml was not found on the classpath
    备注
    MYSQL建表问题(转)
    Class.forName("com.mysql.jdbc.Driver")找不到类
    mySql连接报错
    电脑启动过程
    C++输入输出流--<iostream>详解
    类内部实例化自身可行吗?
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/9219980.html
Copyright © 2011-2022 走看看