传送门:【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 ; }