UVALive - 7345 The Hypnotic Spirals 高等数学+几何知识

The Hypnotic Spirals

高数没学好的后果?其实本来这题就很难写,某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) =21abdθ((aθ)2o2)

= a 2 2 ∫ a b θ 2 d θ =\frac{a^2}{2}\int_{a}^{b}\theta^2 d\theta =2a2abθ2dθ

= a 2 6 ( b 3 − a 3 ) =\frac{a^2}{6}(b^3-a^3) =6a2(b3a3)

有了这个公式,我们就可以求解一个螺旋线在两个射线之间的面积了。

(我开始写的是 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 &lt; θ 0 &lt; 2 π ∗ ( k + 1 ) + θ 0 2\pi*k + \theta_0 &lt; \theta_0 &lt; 2\pi*(k+1) + \theta_0 2πk+θ0<θ0<2π(k+1)+θ0
根据表达式 r = a θ r=a\theta r=aθ可知,带入输入的点r0,可以求得一个 θ ′ \theta^{&#x27;} θ,我们用它减去 θ 0 \theta_0 θ0再除以 2 π 2\pi 2π就会得到一个k的近似值,虽然这个k不一定是准确的,但是最终答案一定在k附近,直接暴力枚举 [ k − 5 , k + 5 ] [k-5,k+5] [k5,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……

因为有太多东西没有考虑全面了!

①首先就是判重问题

[外链图片转存失败(img-NOz4GN1v-1566190944613)(C:\Users\91564\AppData\Roaming\Typora\typora-user-images\1566185258417.png)]

(忽略第三象限两条射线,这是辣鸡网页上带的没法删掉,只看第一象限两条射线)

容易发现,上面两个点代表的是一个封闭区域,但是,但是,左边点的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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值