高数没学好的后果?其实本来这题就很难写,某T现场能写出来有点厉害啊。
首先我们需要知道,当n=0时无解,n !=0时一定有解。
正确性显然,不做讨论。
考虑一个螺旋线,如何求一个螺旋线相对于原点的面积?
高等数学的二重积分:
如果已知所求面积两个端点的角度且角度差不超过 2 ∗ π 2*\pi 2∗π
那么有
S
=
∫
a
b
d
θ
∫
0
a
θ
r
d
r
S=\int_{a}^{b}d\theta\int_{0}^{a\theta}rdr
S=∫abdθ∫0aθrdr
= 1 2 ∫ a b d θ ( ( a θ ) 2 − o 2 ) =\frac{1}{2}\int_{a}^{b}d\theta((a\theta)^2-o^2) =21∫abdθ((aθ)2−o2)
= a 2 2 ∫ a b θ 2 d θ =\frac{a^2}{2}\int_{a}^{b}\theta^2 d\theta =2a2∫abθ2dθ
= a 2 6 ( b 3 − a 3 ) =\frac{a^2}{6}(b^3-a^3) =6a2(b3−a3)
有了这个公式,我们就可以求解一个螺旋线在两个射线之间的面积了。
(我开始写的是 S = ∫ a b d θ ∫ 0 a θ a θ d r S=\int_{a}^{b}d\theta\int_{0}^{a\theta}a\theta dr S=∫abdθ∫0aθaθdr 这显然不对,因为r是个变量,而不是定值)
如果点在下列情况,那么我们需要做的就是求出a,b,c,d的坐标,然后求一个积分。
那么我们现在来考虑,给你一个点
(
r
0
,
θ
0
)
(r_0,\theta_0)
(r0,θ0),如何求它在哪个位置(也就是如何求a,b,c,d),我们要先求出,当前的
θ
\theta
θ是位于哪个k满足
2
π
∗
k
+
θ
0
<
θ
0
<
2
π
∗
(
k
+
1
)
+
θ
0
2\pi*k + \theta_0 < \theta_0 < 2\pi*(k+1) + \theta_0
2π∗k+θ0<θ0<2π∗(k+1)+θ0
根据表达式
r
=
a
θ
r=a\theta
r=aθ可知,带入输入的点r0,可以求得一个
θ
′
\theta^{'}
θ′,我们用它减去
θ
0
\theta_0
θ0再除以
2
π
2\pi
2π就会得到一个k的近似值,虽然这个k不一定是准确的,但是最终答案一定在k附近,直接暴力枚举
[
k
−
5
,
k
+
5
]
[k-5,k+5]
[k−5,k+5]区间,一定能找到合适的k.
既然找到了合适的k,那么下一步就是找它在哪两个射线之间了,如上如。因为给定的 θ 0 \theta_0 θ0就是一个 ( 0 , 2 π ) (0,2\pi) (0,2π)之间的小数,所以也不用做坐标变换,直接判断在哪两个射线之间。这里我特判的几种情况:
①n=1,只有一条射线,那么只需要考虑它在射线前还是射线后
②n>1时特判在第一条射线前和最后一条射线后的情况。
总的来说就是判断第一条射线前和最后一条射线后的情况
同时,一个区域有多个点,我们还需要一个map,想办法把每个区域的特征压缩成几个变量来hash判重。
这里我选择的参数是上面的k和射线编号x。因为它们是独一无二的(特判的大于最后一条边 x为n+1)(并不,后面会讲)
然后现在知道了abcd,直接计算就好~ 然后 Wa……
因为有太多东西没有考虑全面了!
①首先就是判重问题
(忽略第三象限两条射线,这是辣鸡网页上带的没法删掉,只看第一象限两条射线)
容易发现,上面两个点代表的是一个封闭区域,但是,但是,左边点的hash为[0][3] 右边点的hash为[1][1]。所以第一要注意的是hash遇到上述情况的时候要统一转为后面的情况去判重。(即[k-1][n+1]->[k][1])
②对于k=0的情况,我们发现
它其实不需要减去另外的一部分,但是我们求的过程是对通用情况求解的,此时应该特判,当处于这种情况时,置a,b=0.
③对于②中的情况,统一而论其实也不对,为什么?看k=0时,x=n+1的情况(给定点的角度大于最后一条射线)
此时面积为
我们放大看
绿色的地方被多加了!这里再特判一下减去这里的绿色部分就可以了。
④虽然在①中讨论了等价关系,但是只是在记录重复时的等价关系,在考虑③这种情况时,因为前面的步骤都是按照第k层来找的,所以这里直接变换过去是不等的。
理论上都是可以等价过去的,但是对我来说加个特判也不麻烦。
⑤对于某些角度a小于0的点来说,它代表的几何意义不是负角度,而是从0开始,表现在积分上就是 ∫ 0 b \int_{0}^{b} ∫0b,所以此时把a置0就可以了。理论上这种情况只出现在k=1时。
然后就AC了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<ctime>
#define up(i,x,y) for(int i = x;i <= y;i ++)
#define down(i,x,y) for(int i = x;i >= y;i --)
#define mem(a,b) memset((a),(b),sizeof(a))
#define mod(x) ((x)%MOD)
#define lson p<<1
#define rson p<<1|1
using namespace std;
typedef long long ll;
const int SIZE = 500010;
const int INF = 2147483640;
const double eps = 1e-12;
const double pi = acos(-1.0);
typedef double db;
inline void RD(int &x)
{
x = 0; char c; c = getchar();
bool flag = 0;
if(c == '-') flag = 1;
while(c < '0' || c > '9') {if(c == '-') {flag = 1;} c = getchar();}
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0',c = getchar();
}
db a;//螺旋线的系数
db sx[66];//射线
int n,m;
map<ll,map<ll,bool> > vis;
inline int sign(db a) {return a < -eps ? -1 : a > eps; } //返回-1表示a < 0, 1表示a > 0, 0表示a = 0
inline int cmp(db a, db b) {return sign(a-b); } //返回-1表示a < b, 1表示a > b,0表示 a==b
bool check(db r,db sita){for(int i = 1;i <= n;i ++) {if(sita == sx[i]) return false;}return true;}
db r0,sita0;//点的r 和 sita
int cas = 0;
int main(int argc, char const *argv[])
{
int t;
scanf("%d",&t);
while(t--)
{
vis.clear();
cas ++;
scanf("%lf",&a);
scanf("%d",&n);
if(n == 0)
{
printf("Spiral %d: -1\n",cas);
scanf("%d",&n);
for(int i = 1;i <= n;i ++) scanf("%lf%lf",&r0,&r0);
continue;
}
for(int i = 1;i <= n;i ++) scanf("%lf",&sx[i]);
sort(sx+1,sx+1+n);
sx[n+1] = sx[1] + 2*pi;
scanf("%d",&m);
db ans = 0;
for(int i = 1;i <= m;i ++)
{
scanf("%lf%lf",&r0,&sita0);
//先判断
if(!check(r0,sita0)) continue;//说明在射线上 题目保证了不会, 当时没看到
//现在判断在螺旋线的哪一层
db sitapie = r0/a;
db delt = (sitapie - sita0)/(2.0*pi);
db qz = floor(delt);
// if(cmp(delt - qz,0.0) == 0) continue;//如果得到的是一个整数k,那么不是封闭区间
// printf("qaq %lf\n",qz);
db up = 0.0,down = 0.0;
ll jilu1 = 0,jilu2;
for(db k = max(qz-5.0,0.0);k <= qz + 5.0;k += 1.0)//找上下五个k
{
// printf("k: %.6lf\n",k);
db nowr = a*(k*2.0*pi+sita0);
db nowrpie = a*((k+1.0)*2.0*pi+sita0);
if(cmp(nowr,r0) == -1 && cmp(r0,nowrpie) == -1)
{
jilu1 = (ll)(k+1.0);
up = (k+1.0)*2.0*pi;
down = k*2.0*pi;
break;
}
}//上下界已确定
// printf("up,down %.6lf %.6lf\n",up,down);
db lsita,rsita;
if(n == 1)
{
if(cmp(sx[1],sita0) == 1)
{
jilu2 = 1;
rsita = sx[1];
lsita = sx[1] - 2*pi;
}
else
{
jilu2 = 2;
rsita = sx[1]+2*pi;
lsita = sx[1];
}
}
else
{
if(cmp(sx[1],sita0) == 1)
{
// printf("qaq\n");
jilu2 = 1;
rsita = sx[1];
lsita = sx[n] - 2*pi;
}
else
{
for(int i = 2;i <= n+1;i ++)
{
if(cmp(sx[i],sita0) == 1)
{
jilu2 = i;
rsita = sx[i];
lsita = sx[i-1];
break;
}
}
}
}
// printf("jilu: %lld %lld\n",jilu1,jilu2);
//得到了左右角度了
// printf("qaq\n");
// printf("lr sita %lf %lf\n",lsita,rsita);
db t1 = (up+rsita),t2 = (up+lsita),t3 = (down+rsita),t4 = (down+lsita);//我找到的abcd
if(lsita < 0 && cmp(up,0.0) == 1 && cmp(down,0.0) == 0) t4 = 0;//第二部分下限为0
if(cmp(up,0.0) == 0 && cmp(down,0.0) == 0)//k = 0特判,也就是第一层 对应情况②
{
if(jilu2 == n+1)//大于第n条射线角度 对应情况③
{
t3 = sx[1];
t4 = 0;
}
else t3 = 0.0,t4 = 0.0;//如果当前求的是小于第n条射线角度 不需要减 对应普遍情况
}
if(t2 < 0) t2 = 0;//
if(jilu2 == n+1) jilu2 = 1,jilu1 ++;
if(vis[jilu1][jilu2]) continue;
else vis[jilu1][jilu2] = 1;//判断一个封闭区间是否有多个点
// printf("%.6lf %.6lf %.6lf %.6lf\n",t1,t2,t3,t4);
db tmp1 = a*a*(t1*t1*t1-t2*t2*t2)*1.0/6.0;
db tmp2 = a*a*(t3*t3*t3-t4*t4*t4)*1.0/6.0;
ans += tmp1 - tmp2;//积分
}
printf("Spiral %d: %.8lf liters\n",cas,ans/10.0);
}
return 0;
}