题意
有个同心圆,半径分别是,有条直线将这些同心圆切分成等分。
求所有交点两两之间的最短距离的和。
思路
- 圆心到每个点之间的距离是很好算的
- 单个环内部每个点之间的距离和也是很好算的:枚举两点之间的长度,所有的相邻的点的最短距离和会贡献,所有的间隔一个点的最短距离和也是,直到两点恰好在直径两端的时候,贡献,共计,把每个圆的贡献累加,就是
- 考虑外层到内层的点的距离和,首先可以肯定,外层到内层的对应点可以直接走直线,距离,于是就继承了这个点与该内层所有点的最短距离,累加外圈所有点,每多一圈,内层就要多算一遍,而所有的直线也被类加成了立体的锥形。
Solution
#include <bits/stdc++.h> using namespace std; typedef double db; const int N = 510; const db pi = acos(-1); db point[N]; // 表示本层的某个点 到本层所有其他点的距离和 db cir[N]; // 表示当前层的某个点 到本层和内层所有点的距离和 int main() { db n, m, ans = 0; cin >> n >> m; ans += (m > 1) * n * (n + 1) * m; // 直接把圆心到每个点的距离算出来 // 等差序列 和我上面说的“锥形”一样 db th = pi / (1.0 * m); // 表示相邻两个点构成的角度 for (int i = 1; i <= n; i++) { // 由内而外 for (int j = 1; j < m; j++) // 对于一个点,枚举一半的点对 point[i] += min(2.0 * i, th * j * i); // 走圆弧 或者 走半径 point[i] *= 2; // 另外一半的点对 point[i] += 2 * i; //直径 ans += point[i] * m; //本层圆上的 ans += (cir[i - 1] + (i - 1) * 2.0 * m) * 2.0 * m; //本层圆通往内部的 // 内层一共有(i-1)*2*m个点 外层有2*m个点 // 对于每个外层圆上的点 要先降到内层的对应点上 // 每个从外层降到内层的pair 都要加上一个额外的 降到内一层的消耗 cir[i] = cir[i - 1] + (i - 1) * 2.0 * m + point[i]; // 外层+内层 此处做前缀和处理,可避免上面的等差数列求和 //ans+=cir[i] } printf("%.10lf\n", ans); // printf("%.10lf\n", cir[(int)n] * 2 * m + ans); return 0; }
//逆十字 O(n^3) #include<bits/stdc++.h> using namespace std; double pi=acos(-1); int n,m; int main(){ scanf("%d%d",&n,&m); double ans=0; for (int i=1;i<=n;i++) for (int j=i;j<=n;j++) for (int k=0;k<=m;k++) ans+=min(i+j*1.0,i*k*pi/m+j-i)*(k==0||k==m?1:2)*(j==i?1:2); ans*=2*m; if (m!=1){ for (int i=1;i<=n;i++) ans+=4*m*i; } printf("%.15lf\n",ans/2); }
//O(1) #include <bits/stdc++.h> using namespace std; const double pi = acos(-1); int main() { long double n, m, s = 0, k1, k2, t; cin >> n >> m; k1 = (1 + n) * n * n / 2 - n * (n + 1) * (2 * n + 1) / 6; k2 = (1 + n) * n * (0.5 + n) / 2 - n * (n + 1) * (2 * n + 1) / 6; s += (1 + n) * n * m; if (m == 1) s = 0; s += 4 * m * m * k1; t = floor(2 * m / pi); s += (2 * pi * t * (t + 1) + (4 * m * (m - t) - 2 * m) * 2) * k2; cout << fixed << setprecision(10) << s; }