HDOJ 4368

题意:给一个山脉,高低起伏,问向某一个方向旋转一个角度后(角度绝对值属于[0.0,80.0]),所能盛水的容量。

题解:向右转还是向左转差别不大,只需要将山脉reverse就行了。现在考虑向右转,从左往右遍历顶点,以该顶点(设为i)为起始点向右发出一条水平射线,与山脉某一条边碰撞(记为j),在这射线之下所能盛水的面积就等于射线与山脉所组成梯形减去这期间山脉的面积。但还要从记录的这条边的顶点处往左发出一条射线,与山脉某条边k碰撞,这条新射线会与原射线形成一个小梯形(也可能是平行四边形),这部分区域没有计算,所以还应加上。另外,遍历结点的指针就可以直接跳转到j了,因为i到j的区域面积已经计算完了。如此,遍历一遍就可以得出结果。

PS: 打代码速度还是慢了些,最后提交是17:00:37秒,out of contest time 囧,结果赛下一交成为第一个A的,坑爹啊。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define sign(x) ((x)>eps?1:((x)<-eps?(-1):(0))) //符号函数
const double eps=1e-8;
const double PI=acos(-1.0);
const double inf=1e20;
int n;
struct point
{
    double x,y;
    point(){}
    point(double _x,double _y){x=_x,y=_y;}
};
struct seg
{
    point a,b;
    seg(){}
    seg(point _a,point _b){a=_a,b=_b;}
};
point L,R,po[20010];
seg so[20010];
inline double dist(point a,point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void counter (double &x,double &y,double th)
{
//绕原点逆时针旋转th弧度,顺时针则传入-th
    double tx=x,ty=y;
    x=tx*cos(th)-ty*sin(th);
    y=tx*sin(th)+ty*cos(th);
    return;
}
point R_rotata(point p,double ang)
{
    point q;
    ang=ang*PI/180.0;
    q.x=p.x-R.x;q.y=p.y;
    counter(q.x,q.y,ang);
    q.x+=R.x;
    return q;
}
double xmult(double x1,double y1,double x2,double y2)
{
    return x1*y2-x2*y1;
}
point line_intersection(seg u,seg v)
{
    double a1=u.b.y-u.a.y,b1=u.a.x-u.b.x;
    double c1=u.b.y*(-b1)-u.b.x*a1;
    double a2=v.b.y-v.a.y,b2=v.a.x-v.b.x;
    double c2=v.b.y*(-b2)-v.b.x*a2;
    double D=xmult(a1,b1,a2,b2);
    return point(xmult(b1,c1,b2,c2)/D,xmult(c1,a1,c2,a2)/D);
}
int findleft(point p,int k,point &q)
{
    int i;
    for(i=k;i>=0;i--)
    {
        if(so[i].b.y>p.y-eps)
            break;
    }
    if(i<0)
        return i;
    else if(i%2==1)
    {
        q=line_intersection(seg(p,point(p.x-1.0,p.y)),so[i]);
        return i;//与右边相遇
    }
    else
    {
        q=line_intersection(seg(p,point(p.x-1.0,p.y)),seg(so[i].b,so[i+1].b));
        return i;//与头顶相遇
    }
}
int findright(point p,int k,point &q)
{
    int i;
    for(i=k;i<n;i++)
    {
        if(so[i].b.y>p.y-eps)
            break;
    }
    if(i>=n)
        return i;
    else
    {
        q=line_intersection(seg(p,point(p.x+1.0,p.y)),so[i]);
        return i;
    }
}
double h[10010];
int main()
{
    int num;
    double angle;
    while(scanf("%d%lf",&num,&angle)!=EOF)
    {
        n=num*2;
        R.x=(double)num,R.y=0;
        for(int i=0;i<num;i++)
        {
            scanf("%lf",&h[i]);
        }
        if(angle>eps)
        {
            reverse(h,h+num);
            angle=-angle;
        }
        for(int i=0;i<num;i++)
        {
            po[2*i].x=(double)i;
            po[2*i+1].x=i+1.0;
            po[2*i].y=po[2*i+1].y=h[i];
        }
        if(angle<-eps)
        {
            double ans=0;
            for(int i=0;i<n;i++)
            {
                so[i].a=R_rotata(point(po[i].x,0.0),angle);
                so[i].b=R_rotata(po[i],angle);
            }
            point q1,q2;
            int i,j,k,tp;
            for(i=0;i<n;)
            {
                j=findright(so[i].b,i+1,q1);
                if(j>=n)
                {
                    i++;
                    continue;
                }
                ans=ans+(dist(so[i].a,so[i].b)+dist(so[j].a,q1))*(po[j].x-po[i].x)/2.0;
                if(i&1)
                    for(tp=i+1;tp<j;tp+=2)
                        ans-=po[tp].y;
                else
                    for(tp=i;tp<j;tp+=2)
                        ans=ans-po[tp].y;
                k=findleft(so[j].b,i-1,q2);
                if(k<0)
                {
                    i=j;
                    continue;
                }
                if(k&1)
                    ans=ans+dist(q2,so[j].b)*(so[j].b.y-so[i].b.y);
                else
                    ans=ans+(dist(q1,so[i].b)+dist(q2,so[j].b))*(so[j].b.y-so[i].b.y)/2.0;
                i=j;
            }
            printf("%.2lf\n",ans);
        }
        else
        {
            double ans=0;
            for(int i=0;i<n;i++)
            {
                so[i].a=point(po[i].x,0.0);
                so[i].b=po[i];
            }
            point q1,q2;
            int i,j,k,tp;
            for(i=1;i<n;)
            {
                j=findright(so[i].b,i+1,q1);
                if(j>=n)
                {
                    i+=2;
                    continue;
                }
                ans=ans+(dist(so[i].a,so[i].b)+dist(so[j].a,q1))*(po[j].x-po[i].x)/2.0;
                for(tp=i+1;tp<j;tp+=2)
                    ans-=po[tp].y;
                k=findleft(so[j].b,i-1,q2);
                if(k<0)
                {
                    i=j+1;
                    continue;
                }
                ans=ans+dist(q2,so[j].b)*(so[j].b.y-so[i].b.y);
                i=j+1;
            }
            printf("%.2lf\n",ans);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值