链接
题解
一看到
,首先想能不能
,
有两种思路,要么对每个点都做
的处理,要么以针对边集做
的算法
如果按照点想的话,显然每个点可以按照从小到大扫描它的所有出边,当半径等于某条边的长度时,这个星球的
或者
就会改变,那么就给他新建一个“事件”,表示当
到达这个数的时候,某个点的
或
会改变。记录下这种改变,每个点最多有
个事件,按照半径的大小排个序就好了
看了题解后发现,有时候可以换一种思路,把视野扩大一点,看下边集上能不能做一些处理
显然
的最小值肯定能是某条边的长度,我们把边按照从小到大排个序,然后针对每条边的两个端点
维护一下
和
,统计个最大值就行了,复杂度
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cmath>
#define ll long long
#define eps 1e-8
#define maxn 1010
#define cl(x) memset(x,0,sizeof(x))
#define sqr(x) ((x)*(x))
using namespace std;
int N_same[maxn], N_dif[maxn], p[maxn], anscnt, now, x[maxn], y[maxn], z[maxn], ansr, M, N;
struct R
{
int a, b, dist;
bool operator<(R &x){return dist<x.dist;}
bool operator!=(R &x){return dist!=x.dist;}
}r[maxn*maxn];
int read(int x=0)
{
char c, f=1;
for(c=getchar();!isdigit(c) and c^-1;c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
return f*x;
}
void init()
{
int i, j;
for(i=1;i<=N;i++)x[i]=read(), y[i]=read(), z[i]=read(), p[i]=read();
for(i=1,M=0;i<=N;i++)for(j=i+1;j<=N;j++)r[++M]=(R){i,j,sqr(x[i]-x[j])+sqr(y[i]-y[j])+sqr(z[i]-z[j])};
sort(r+1,r+M+1);
}
void solve()
{
int i, j, a, b;
anscnt=now=ansr=0;
for(i=1;i<=N;i++)N_same[i]=1, N_dif[i]=0;
for(i=1;i<=M;i++)
{
a=r[i].a, b=r[i].b;
if(p[a]==p[b])
{
N_same[a]++, N_same[b]++;
if(N_same[a]==N_dif[a])now--;
if(N_same[b]==N_dif[b])now--;
}
else
{
N_dif[a]++, N_dif[b]++;
if(N_dif[a]==N_same[a]+1)now++;
if(N_dif[b]==N_same[b]+1)now++;
}
if((r[i]!=r[i+1] or i==M) and now>anscnt)anscnt=now, ansr=r[i].dist;
}
printf("%d\n%.10lf\n",anscnt,sqrt(ansr));
}
int main()
{
while(~scanf("%d",&N))
{
init();
solve();
}
return 0;
}