1. 程式人生 > 實用技巧 >第 45 屆國際大學生程式設計競賽(ICPC)亞洲區域賽(上海)I.Sky Garden(幾何/思維)

第 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\n", ans);
	return 0;
}