题意:
n个同心圆,第i个圆的半径为i,m条直线将每个圆分割成相等的2*m个部分,每条直线和每个圆都有两个交点。 P P P是交点的集合,求集合中任何两个点的最短距离的和。
- m m m条直线将每个圆分成均等的 2 m 2m 2m个部分,圆上有 2 m 2m 2m个点
- 首先我们考虑同一个圆上两两点的最短距离:要么是弧长,要么是直径。所以我们直接遍历判断,求得点1,到其他点的最短距离 r e s res res, 然后有 2 m 2m 2m个点,因为每个距离都被算了两遍,所以这个圆上两两点的最短距离就是 r e s ∗ 2 m 2 = r e s ∗ m \frac {res*2m}{2}=res*m 2res∗2m=res∗m
- 然后我们考虑从第j个圆到第i个圆上的点的距离,我们以下图为例子,点A要到1,2,3,4的最短距离,首先必定经过半径差d,点A点2,3,4的最短距离,等价于点1到点2,3,4的距离加上d..所以点A到点1,2,3,4的最短距离等于
d
∗
2
m
+
r
e
s
d*2m+res
d∗2m+res, 由于外圆上也有
2
m
2m
2m个点,对应内圆的
2
m
2m
2m个点,所以外圆到内圆点的距离之和为
2
m
∗
2
m
∗
d
+
r
e
s
∗
2
m
2m*2m*d + res*2m
2m∗2m∗d+res∗2m
- 如果m>1时,圆心也会出现交点5,点5到点1,2,3,4的最短距离就是
2
m
∗
r
2m*r
2m∗r
- 把以上计算结果加起来就是答案
代码
提供两份
#include<bits/stdc++.h>
using namespace std;
const double PI = acos(-1);
int main()
{
int n,m;
scanf("%d%d",&n,&m);
double ans=0;
//每个圆被分成了2*m个部分
int sum=2*m;
//枚举每个圆的半径
for(int k=1; k<=n; k++)
{
double r=k;
//因为圆被分成了2*m个部分,所以每个部分的弧长是tmp
double tmp=2*PI*r/sum;
//当前圆两两点直接的距离之和
double res=0;
//这里计算的是圆上的点到圆心的距离
//前提是圆心要有交点,故需要判断m>1
if(m>1) ans+=sum*r;
for(int j=2; j<=sum; j++)
{
int d=abs(1-j);
if(d>m) d=sum-d;
//圆上两个点之间的距离要么是,弧长最短,要么是直径最短
res+=min(d*tmp,2*r);
}
res=res*2*m/2;
//这里不仅要加上res,还要加上res对后面圆的贡献
ans+=res+res*(n-k)*2;
}
//计算两两圆直接的半径差
for(int i=1; i<=n; i++)
{
for(int j=i+1; j<=n; j++)
{
ans+=abs(i-j)*(2*m*2*m);
}
}
printf("%f\n",ans);
return 0;
}
#include<bits/stdc++.h>
using namespace std;
const double PI = acos(-1);
int main()
{
int n,m;
scanf("%d%d",&n,&m);
double ans=0;
int sum=2*m;
for(int k=1; k<=n; k++)
{
double r=k;
double tmp=2*PI*r/sum;
double res=0;
if(m>1) ans+=sum*r;
for(int i=1; i<=sum; i++)
{
for(int j=i+1; j<=sum; j++)
{
int d=abs(i-j);
if(d>m) d=sum-d;
res+=min(d*tmp,2*r);
}
}
ans+=res+res*(n-k)*2;
}
for(int i=1; i<=n; i++)
{
for(int j=i+1; j<=n; j++)
{
ans+=abs(i-j)*(2*m*2*m);
}
}
printf("%f\n",ans);
return 0;
}