终于迎来了长春的重现。赛场上太多的遗憾,比赛的时候唯一看都没看一眼的题就是这个题,太可惜了。连同B题一起,为了抢个FB对不住打重现赛的童鞋们了。。。。。求放过,也不知道这个时候写题解算不算违规呢哈哈。。。。。。
题意:按顺序给出一个多边形,以多边形的每个顶点为圆心作圆,使得任意两相邻点对应的圆相切,求所有圆面积总和的最小值。
画个图算两下就出来了,发现只需要对n的奇偶性进行讨论即可。
设每个点为pi(0 <= i <= n - 1),对应圆的半径为ri,pi和p(i + 1)(默认pn为p0)的距离为di,则很显然我们得到了n个方程:
Fi:ri + r(i + 1) = di(0 <= i <= n - 1)。
通过这个方程我们发现,全部加起来除以2即得到了所有ri的和。当n为奇数时,将i为奇数的方程Fi相加可以得到r1 + r2 + ...... + r(n - 1)的和,再用总和减去它即得到了r0,也就是说,n为奇数时,这个方程组有唯一解,所以,解出所有的解,只要所有解都非负即可算出答案,如果有负的就IMPOSSIBLE。当n为偶数时,i为偶数的方程相加和i为奇数的方程相加的结果应该是一样的(都等于所有ri之和)。所以先算这两个和,如果这两个和不相等,也是IMPOSSIBLE。其次,我们从第二个方程起,可以将每个ri都变成与r0相关的式子,这样,所有圆的面积都可以用r0表示,最后的总面积是关于r0的二次函数,直接求解最小值即可。设ri = ai*r0 + bi,
则r(i + 1) = di - ri = -ai*r0 - bi + di。所以a(i + 1) = -ai,b(i + 1) = - bi + di。a0 = 1,b0 = 0。这样求出每个ai和bi(可以发现ai只有1和-1),然后面积和 = pi*sigma(ri^2,0 <= i <= n - 1) = A*r0^2 + B*r0 + C,算出A,B,C的值再根据每个ri的范围(0 <= ri <= min(di,d(i + 1))求出r0的取值范围(即维护区间的左右端点),如果区间右端点小于左端点,则无解IMPOSSIBLE。否则根据二次函数性质算出区间内最低点即可。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <stack>
#include <queue>
#include <set>
using namespace std;
const int MAX = 1e4 + 5;
const double pi = acos(-1.0);
const double eps = 1e-6;
int n, x[MAX], y[MAX];
double d[MAX], r[MAX];
int sgn(double t)
{
return (t > eps) - (t < -eps);
}
double f(double a, double b, double c, double t)
{
return a*t*t + b*t + c;
}
void input()
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%d%d", &x[i], &y[i]);
}
void solve()
{
x[n] = x[0];
y[n] = y[0];
for(int i = 0; i < n; i++)
d[i] = sqrt((x[i + 1] - x[i])*(x[i + 1] - x[i]) + (y[i + 1] - y[i])*(y[i + 1] - y[i]));
if(n&1)
{
double all = 0, other = 0, ans = 0;
for(int i = 0; i < n; i++)
{
all += d[i];
if(i&1)
other += d[i];
}
r[0] = all/2 - other;
if(sgn(r[0]) < 0)
{
printf("IMPOSSIBLE\n");
return;
}
ans += r[0]*r[0];
for(int i = 1; i < n; i++)
{
r[i] = d[i - 1] - r[i - 1];
if(sgn(r[i]) < 0)
{
printf("IMPOSSIBLE\n");
return;
}
ans += r[i]*r[i];
}
printf("%.2lf\n", pi*ans);
for(int i = 0; i < n; i++)
printf("%.2lf\n", r[i]);
}
else
{
double temp = 0;
for(int i = 0; i < n; i += 2)
temp += d[i] - d[i + 1];
if(sgn(temp) != 0)
{
printf("IMPOSSIBLE\n");
return;
}
int k = 1;
double b = 0, L = 0, R = d[0];
double A = 1, B = 0, C = 0;
for(int i = 1; i < n; i++)
{
k = -k;
b = -b + d[i - 1];
if(k == 1)
L = max(L, -b);
else
R = min(R, b);
A++;
B += 2*k*b;
C += b*b;
}
if(sgn(L - R) > 0)
{
printf("IMPOSSIBLE\n");
return;
}
double mid = -B/(2*A), ans;
if(sgn(L - mid) >= 0)
{
ans = f(A, B, C, L);
r[0] = L;
}
if(sgn(R - mid) <= 0)
{
ans = f(A, B, C, R);
r[0] = R;
}
if(sgn(mid - L) >= 0 && sgn(mid - R) <= 0)
{
ans = f(A, B, C, mid);
r[0] = mid;
}
printf("%.2lf\n", pi*ans);
for(int i = 0; i < n; i++)
{
if(i)
r[i] = d[i - 1] - r[i - 1];
printf("%.2lf\n", r[i]);
}
}
}
int main()
{
int T;
scanf("%d", &T);
for(int t = 1; t <= T; t++)
{
input();
solve();
}
}