【ZOJ】3832 Tilt Cylinder 积分题——simpson

传送门:【ZOJ】3832 Tilt Cylinder


题目分析:最近一直在补题。。都没写题解。。。。

本题我们将圆柱体顺时针旋转α度,然后我们垂直将圆柱体切开得到一个圆,设阴影部分为水,则当阴影部分的高度不高于半径R时,阴影部分的面积为扇形面积-三角形面积(弓形面积),否则阴影部分面积为圆面积减去上半部分没有阴影部分的弓形面积。此面积是和横坐标有关的,是一个以横坐标为自变量的积分,用simpson积分暴力求即可。

重点还是分类讨论,将体积分成三类:

1.圆柱体(横坐标L1~R1)

2.水平线与圆柱体相交的左端点(Left,upper)到半径高度所在的位置(横坐【L2,R2】)

3.半径高度所在的位置到水平线与圆柱体相交的右端点(Right,lower)(横坐标【L3,R3】)

(x,y)为端点坐标。

现在分情况讨论。

对于左端点:

如果h / cos(α)> 2 * R,说明水平线与圆柱体左上部分相交

此时L1 = 0,R1 = L2 = Left =(h / cos(α - (2 * R))/ tan(α),upper = 2 * R。

否则和圆柱体左下部分相交

此时L1 = R1 = L2 = Left = 0,upper = h / cos(α)。

对于右端点:

如果h / sinα)> H,说明水平线与圆柱体右上部分相交

此时R3 = Right = H,lower = (h / sin(α) - H) * tan(α)。

否则水平线与圆柱体右下部分相交

此时R3 = Right = h / sin(α),lower = 0。

知道左端点坐标以及右端点坐标我们可以确定一条直线方程(x - x1)/(x2 - x1)=(y - y1)/(y2 - y1),凭此直线方程我们可以将x带入求y,也可以将y带入求x。(x1,y1)为左端点坐标,(x2,y2)为右端点坐标。

然后我们将R作为高度带入直线方程求出此时的横坐标R2(L3),根据x我们可以将直线与圆柱体相交的区间分成两部分【L2,R2】,【L3,R3】积分,一部分为纵坐标大于R的令一部分为小于等于R的,分别对这两个区间做积分求出体积,最后加上【L1,R1】内圆柱体部分的积分就是所求积分。


如果水平线与左下部分相交,则L1=R1=0,无圆柱体体积。

如果半径R在Left的左边,则此时L1=R1=L2=R2=0,只有纵坐标小于等于R部分的积分。

如果半径R在Right的右边,则此时R2=L3=R3=Right,没有纵坐标小于等于R部分的积分。


代码如下:


#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

typedef long long LL ;

#define rep( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define For( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define rev( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )

const double pi = acos ( -1.0 ) ;
const double eps = 1e-5 ;

double Left , Right ;
double lower , upper ;
double H , R , h , a ;

double get_y ( double x ) {
	return ( x - Left ) * ( lower - upper ) / ( Right - Left ) + upper ;
}

double get_x ( double y ) {
	return ( y - upper ) * ( Right - Left ) / ( lower - upper ) + Left ;
}

int dcmp ( double x ) {
	return ( x > eps ) - ( x < -eps ) ;
}

double f1 ( double h ) {
	double a = R ;
	double b = R ;
	double c = 2.0 * sqrt ( R * R - ( R - h ) * ( R - h ) ) ;
	if ( dcmp ( c ) == 0 ) return 0 ;
	double rad = ( a * a + b * b - c * c ) / ( 2.0 * a * b ) ;
	return R * R * acos ( rad ) / 2.0 - ( R - h ) * c / 2.0 ;
}

double f2 ( double h ) {
	return R * R * pi - f1 ( 2.0 * R - h ) ;
}

double simpson1 ( double a , double b ) {
	double c = ( a + b ) / 2 ;
	double A = get_y ( a ) ;
	double B = get_y ( b ) ;
	double C = get_y ( c ) ;
	return ( f1 ( A ) + 4 * f1 ( C ) + f1 ( B ) ) * ( b - a ) / 6 ;
}

double calc1 ( double a , double b , double eps , double res ) {
	double c = ( a + b ) / 2 ;
	double L = simpson1 ( a , c ) , R = simpson1 ( c , b ) ;
	if ( fabs ( L + R - res ) < 15 * eps ) return L + R + ( L + R - res ) / 15 ;
	return calc1 ( a , c , eps / 2 , L ) + calc1 ( c , b , eps / 2 , R ) ;
}

double calc1 ( double a , double b , double eps ) {
	return b >= a ? calc1 ( a , b , eps , simpson1 ( a , b ) ) : 0 ;
}

double simpson2 ( double a , double b ) {
	double c = ( a + b ) / 2 ;
	double A = get_y ( a ) ;
	double B = get_y ( b ) ;
	double C = get_y ( c ) ;
	return ( f2 ( A ) + 4 * f2 ( C ) + f2 ( B ) ) * ( b - a ) / 6 ;
}
double calc2(double a , double b , double eps , double res ) {
	double c = (a + b) / 2;
	double L = simpson2 ( a , c ) , R = simpson2 ( c , b ) ;
	if ( fabs ( L + R - res ) < 15 * eps ) return L + R + ( L + R - res ) / 15 ;
	return calc2 ( a , c , eps / 2 , L ) + calc2 ( c , b , eps / 2 , R ) ;
}

double calc2 ( double a , double b , double eps ) {
	return b >= a ? calc2 ( a , b , eps , simpson2 ( a , b ) ) : 0 ;
}



void solve () {
	a = a / 180.0 * pi ;
	if ( dcmp ( a ) == 0 ) {
		if ( h > R ) printf ( "%.10f\n" , f2 ( h ) * H ) ;
		else printf ( "%.10f\n" , f1 ( h ) * H ) ;
		return ;
	} if ( dcmp ( a - pi / 2.0 ) == 0 ) {
		printf ( "%.10f\n" , pi * R * R * h ) ;
		return ;
	}
	if ( h / sin ( a ) > H ) {
		Right = H ;
		lower = ( h / sin ( a ) - H ) * tan ( a ) ;
	} else {
		Right = h / sin ( a ) ;
		lower = 0 ;
	}
	if ( h / cos ( a ) > ( 2.0 * R ) ) {
		upper = 2.0 * R ;
		Left = ( h / cos ( a ) - ( 2.0 * R ) ) / tan ( a ) ;
	} else {
		upper = h / cos ( a ) ;
		Left = 0 ;
	}
	double mid = min ( max ( get_x ( R ) , Left ) , Right ) ;
	double ans = 0 ;
	double L1 = 0 , R1 = Left ;
	double L2 = Left , R2 = mid ;
	double L3 = mid , R3 = Right ;
	ans += ( R1 - L1 ) * ( R * R * pi ) ;
	ans += calc2 ( L2 , R2 , eps ) ;
	ans += calc1 ( L3 , R3 , eps ) ;
	printf ( "%.10f\n" , ans ) ;
}

int main () {
	while ( ~scanf ( "%lf%lf%lf%lf" , &R , &H , &h , &a ) ) solve () ;
	return 0 ;
}


你可能感兴趣的:(HDU)