2015 ACM/ICPC Asia Regional Changchun:E - Rebuild

根据题意,每相邻两个点所产生的圆是相切的,所以可以根据题意建立一个方程
假设每一个点半径为r,每相邻两个点的距离是d(所有d都可求)
那么就有
r1+r2=d1
r2+r3=d2
r3+r4=d3
...
r(n-1)+r(n)=d(n-1)
r(n)+r(1)=d(n)

一:n为奇数
可以发现在n为奇数的时候联立所有方程,方程组是有解的
举个例子,n为3的时候有:
r1+r2=d1
r2+r3=d2
r3+r1=d3
联立解得2r1=d3-d2+d1
所以r1可以直接解出来,那么按序可以把所有的r都解出来
但是在计算过程中还需要判断r是否全为正数,全为正数才有解,否则无解
二:n为偶数
n为偶数的时候联立到最后一项发现不能解出某个点的r值
举个例子,n为4的时候有:
1.r1+r2=d1
2.r2+r3=d2
3.r3+r4=d3
4.r4+r1=d4
联立解得
r4+r1=d3-d2+d1
r4+r1=d4
不能解出某个点的r值,所以不能直接得到r1,按理来说只能枚举r1可能出现的所有值
但是如果要让面积最小,根据圆的面积公式
s=πr(1)²+πr(2)²+..+πr(n)²
由于其他所有的r都可以转化为r1的形式所以s其实是关于r1的一个一元二次方程,可以想到用三分来找到极小值
接下来只要知道r1的范围就能得到使s最小的r1了
    求r1范围
    由1.     得r1=r1                r1>=0
    由1.2.   得r1=d1-r2               由于r2>=0,所以r1<=d1
    由1.2.3. 得r1=d1-d2+r3>=0        由于r3>=0,所以r1>=d1-d2
    由1.2.3.4得r1=d1-d2+d3-r4>=0    由于r4>=0,所以r1<=d1-d2+d3
所求r1的范围就是三分的范围(PS:如果求到的左端点l大于右端点r说明无解)
PS1:由于我的函数double cal(double r)中只是由d1,d2,d3...d(n-1)推出来的所有r
    dn并没有用上所以在最后判断答案的时候为了体现dn,最后做了一个dn是否等于r1+rn的判定(注意是浮点数的判定)
PS2:我没注意到的一点就是浮点数在跟0进行比较的话,为了保险起见通常会设定一个精度使得fabs(x)<eps

#include <map>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define pp pop_back()
#define int long long
#define lowbit(x) ((x)&(-x))
#define double long double
#define sf(x) scanf("%lld",&x)
#define sff(x,y) scanf("%lld %lld",&x,&y)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef unsigned long long ULL;
typedef pair<int,int>PII;
const int N=1e4+10,M=2*N,INF=4e18;
const double PI=acos(-1),eps=1e-5;
int n,m;
PII q[N];
double nowr[N],dist[N];
bool bl=false;
double get(PII a,PII b)
{
	int aa=a.first-b.first;
	int bb=a.second-b.second;
	return sqrt(aa*aa+bb*bb);
}
int f(double x)
{
	if(fabs(x)<eps||x>0)return 0;
	return -1;
}
double cal(double r)
{
	nowr[1]=r;
	double sum=r*r;
	for(int i=2;i<=n;i++)
		nowr[i]=dist[i-1]-nowr[i-1],sum+=nowr[i]*nowr[i];
	return sum*PI;
}
void solve()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)scanf("%lld %lld",&q[i].fi,&q[i].se);
	for(int i=1;i<=n;i++)
		if(i!=n)dist[i]=get(q[i],q[i+1]);
	else dist[i]=get(q[i],q[1]);
	if(n&1)
	{
		double now=0;
		for(int i=1;i<=n;i++)
		{		
			if(i&1)now+=dist[i];
			else now-=dist[i];
		}
		bl=false;
		double res=cal(now/2);
		for(int i=1;i<=n;i++)
			if(f(nowr[i])<0)
			{
				bl=true;
				break;
			}
		if(bl)
		{
			puts("IMPOSSIBLE");
			return;
		}
		printf("%.2Lf\n",res);
		for(int i=1;i<=n;i++)printf("%.2Lf\n",nowr[i]);
	}
	else
	{
		double now=0;
		double l=-INF,r=INF;
		bl=false;
		for(int i=1;i<=n;i++)
		{
			if(i&1)now+=dist[i],r=min(r,now);
			else now-=dist[i],l=max(l,now);
		}
		if(l<0||l>r)
		{
			puts("IMPOSSIBLE");
			return;
		}
		while(r-l>eps)
		{
			double k=(r-l)/3;
			double midr=r-k;
			double midl=l+k;
			if(cal(midl)>=cal(midr))l=midl;
			else r=midr;
		}
		for(int i=1;i<=n;i++)
			if(f(nowr[i])<0)
			{
				bl=true;
				break;
			}
		if(nowr[1]+nowr[n]-dist[n]>eps)//||fabs(nowr[1]+nowr[n]-dist[n])<-eps)
		{
			puts("IMPOSSIBLE");
			return;
		}
		printf("%.2Lf\n",cal(l));
		for(int i=1;i<=n;i++)printf("%.2Lf\n",nowr[i]);
	}
	return ;
}
signed main()
{
//	IOS;
	int T=1;
	scanf("%lld",&T);
	while(T--)
		solve();
	return 0;
}

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值