第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(上海)I.Sky Garden(几何/思维)

链接:https://ac.nowcoder.com/acm/contest/9925/I
来源:牛客网

题目描述

Prof. Du and Prof. Pang plan to build a sky garden near the city of Allin. In the garden, there will be a plant maze consisting of straight and circular roads.

On the blueprint of the plant maze, Prof. Du draws nn circles indicating the circular roads. All of them have center (0,0)(0,0). The radius of the ii-th circle is ii.

Meanwhile, Prof. Pang draws mm lines on the blueprint indicating the straight roads. All of the lines pass through (0,0)(0,0). Each circle is divided into 2m2m parts with equal lengths by these lines.

Let QQ be the set of the n+mn+m roads. Let PP be the set of all intersections of two different roads in QQ. Note that each circular road and each straight road have two intersections.

For two different points a∈Pa∈P and b∈Pb∈P, we define dis({a,b})dis({a,b}) to be the shortest distance one needs to walk from aa to bb along the roads. Please calculate the sum of dis({a,b})dis({a,b}) for all {a,b}⊆P{a,b}⊆P.

输入描述:

The only line contains two integers n,m (1≤n,m≤500)n,m (1≤n,m≤500).

输出描述:

Output one number -- the sum of the distances between every pair of points in PP.

Your answer is considered correct if its absolute or relative error does not exceed 10−610−6.

示例1

输入

复制

1 2

输出

复制

14.2831853072

说明

dis(p1,p2)=dis(p2,p3)=dis(p3,p4)=dis(p1,p4)=π2dis(p1,p2)=dis(p2,p3)=dis(p3,p4)=dis(p1,p4)=2π
dis(p1,p5)=dis(p2,p5)=dis(p3,p5)=dis(p4,p5)=1dis(p1,p5)=dis(p2,p5)=dis(p3,p5)=dis(p4,p5)=1

dis(p1,p3)=dis(p2,p4)=2dis(p1,p3)=dis(p2,p4)=2

示例2

输入

复制

2 3

输出

复制

175.4159265359

大意就是n个等间距的同心圆被m条过原点的直线均分(最对称的情况),其中圆和直线以及直线和直线的交点构成了一个点集,问点集内的两两最短距离的和是多少(其中最短距离只能是从直线或圆上走出来的)。

首先考虑n=1的简化情况:

这时候选取圆上一个点,到其他点的话要么走圆弧,要么走两个半径的距离(其他情况不可能更优),这时候就需要比较一下从一个点到哪些点的距离小于等于两个半径的长度,这些点就都走圆弧(这部分的距离和可以用圆弧长+等差数列求和求出来),其他点走两个半径的长度,这两部分相加就求出了一个点到圆上其他各个点的最短距离的和。然后一个圆上有2*m个点,最终把求出来的数乘以2m再除以2就是圆上点两两之间最短距离的和了(容斥)。

然后推广到n > 1的情况,假设现在在第n层,且第n层圆上点两两之间最短距离的和求出来了,就考虑取一个点到内层各个点的最短距离的和。首先从这个点出发到第n - 1层一定会走1个单位距离(其他情况不可能更优),这样就变成了在n - 1层一个点到圆上其他各个点的距离和了,然后因为第n层有2m个点,这部分答案还要乘以2m,同理到第n - 2层会先走n - 2个单位距离....

因此最终的思路就是第一重循环从第n层往下遍历,循环内先求出这层圆上个点两两最短距离和,然后第二重循环求当前层上一点到内层各点的最短距离的和(最后别忘乘以当前层节点数2m)。注意不要忽略原点:

当m = 1的时候不用管(因为一条直线不在原点产生交点,因为这case通过率一直是92%....),m不等于1的时候要ans += 1.0 * 2 * m * (1 + n) * n / 2。

#include <iostream>
#include <cmath>
using namespace std;
int n, m;
double ans = 0;
const double PI = acos(-1.0);
int main()
{
	freopen("data.txt", "r", stdin);
	cin >> n >> m;
	for(int i = n; i >= 1; i--)
	{
		//先计算外层一圈 再计算外层每个点到内层
		double tmp_sum = 0;
		double h = PI * 1.0 * i / (1.0 * m);//一小段弧长
		int point_in = 2 * ((int)(2.0 * i / (1.0 * h)));
		int point_out = 2 * m - 1 - point_in;
		tmp_sum += 1.0 * 2 * i * point_out;
		tmp_sum += 2.0 * h * ((1 + point_in / 2) * (point_in / 2) / 2);
		tmp_sum = tmp_sum * m;//每圈有2m个点
		for(int j = i - 1; j >= 1; j--)//加上当前层到内层每一个点的距离
		{
			double tmp = 0;
			tmp += 1.0 * (i - j) * 2 * m;
			tmp += 1.0 * 2 * j * point_out;
			double hh = 1.0 * PI * j / m;
			tmp += 2.0 * hh * ((1 + point_in / 2) * (point_in / 2) / 2);
			tmp *= 2.0 * m;
			tmp_sum += tmp;
		}
		ans += tmp_sum;
	}
	if(m != 1) ans += 1.0 * m * (1 + n) * n;//所有点到原点的距离
	//m=1 就不用算到原点的了!!!!!
	printf("%.7lf
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/lipoicyclic/p/14133146.html