题意:给一个山脉,高低起伏,问向某一个方向旋转一个角度后(角度绝对值属于[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;
}