UVALive 2963 Hypertransmission

链接

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=964

题解

一看到 n=1000 n = 1000 ,首先想能不能 O(n2) O ( n 2 )
O(n2) O ( n 2 ) 有两种思路,要么对每个点都做 O(n) O ( n ) 的处理,要么以针对边集做 O(n2) O ( n 2 ) 的算法
如果按照点想的话,显然每个点可以按照从小到大扫描它的所有出边,当半径等于某条边的长度时,这个星球的 N N − 或者 N+ N + 就会改变,那么就给他新建一个“事件”,表示当 r r 到达这个数的时候,某个点的N N+ N + 会改变。记录下这种改变,每个点最多有 n n 个事件,按照半径的大小排个序就好了
看了题解后发现,有时候可以换一种思路,把视野扩大一点,看下边集上能不能做一些处理
显然r的最小值肯定能是某条边的长度,我们把边按照从小到大排个序,然后针对每条边的两个端点 O(1) O ( 1 ) 维护一下 N+ N + N N − ,统计个最大值就行了,复杂度 O(n2) O ( n 2 )

代码

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值