【例题】【二分答案+2-SAT】NKOJ3814 调整卫星

9 篇文章 0 订阅
1 篇文章 0 订阅

NKOJ3814 调整卫星
时间限制 : - MS 空间限制 : 165536 KB
评测说明 : 1000ms

问题描述
某国上空有 n 颗卫星,…。每颗卫星有一次调整的机会,方式是卫星可以向 y 轴正方向移 动或 y 轴负方向移动指定长度。现在请你给每个卫星发出指令,让卫星向正方向移动或是负 方向移动,使得最后卫星分布中最近的两颗卫星的距离最远。 请输出最近两颗卫星距离的平方。

输入格式
输入第一行为一个数 n,表示有 n 颗卫星。接
下来的 n 行描述了 n 颗卫星,每行三个整数分别表示卫星的坐标 x,y 和卫星可以移 动的距离。

输出格式
输出一个整数表示答案。

样例输入
3
1 6 4
2 6 3
1000 5 3

样例输出
50

提示
【样例解释】 三颗卫星,坐标分别为(1,6) (2,6) (1000,5),第 1 颗卫星可以向上或向 下移 4 个单位,第 2 颗卫星可以向上或向下移 3 个单位,第 3 颗卫星可以向上或向下移 3 个单位。那么最好的方案是一号卫星向上移,二号卫星向下移,三号卫星随意。或是一号卫 星向上移,二号卫星向下移,三号卫星随意。这样移动后最近的卫星为一号卫星和二号卫星, 距离的平方为 7*7+1*1=50
【数据范围】
30% n<=10,坐标绝对值与移动距离小于 100
60% n<=100,坐标绝对值与移动距离绝对值小于 1000
100% n<=1000,坐标绝对值与任意移动距离均在 32 位整数范围内,无论如何调整, 任意两颗卫星的距离均小于 2^31.

思路:
首先要读出题目意思是:每颗卫星必须移动,且只能向上或向下固定距离
出题人的意思是二分答案+2-SAT验证…..

#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
#define ll long long
const int need=1006;

int n,nn;

//.................................................
struct fy{int a,b;} w[need<<1];

ll dis[need<<1][need<<1];
ll dis_(fy a,fy b)//x,y在int 范围内,dis在long long范围内 
{
    int x=a.a-b.a,y=a.b-b.b;
    return (ll)x*x+(ll)y*y;
} 
//.................................................
int fi[need<<1],tot_=1;
struct edge
{
    int en,la;
    edge(int a=0,int b=0){en=a,la=b;}
} ;
vector<edge> ve;
#define pb(a,b) push_back(edge(a,b))

void add(int a,int b)
{
    tot_++;
    ve.pb(b,fi[a]);
    fi[a]=tot_;
}
//.................................................
int dfn[need<<1],low[need<<1],id[need<<1],visttime,scc;
int st[need<<1],top;
bool ins[need<<1];

void tarjan(int x)
{
    dfn[x]=low[x]=++visttime;
    st[++top]=x;ins[x]=true;
    int y;
    for(int t=fi[x];t;t=ve[t].la)
    {
        y=ve[t].en;
        if(dfn[y]==0) 
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(ins[y]) low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x])
    {
        scc++;
        do{
            y=st[top],top--;
            ins[y]=false;
            id[y]=scc;
        }while(x!=y);
    }
}

bool judge(ll a)
{
    visttime=top=scc=0;
    tot_=1;
    for(int i=1;i<=nn;i++) dfn[i]=low[i]=ins[i]=id[i]=fi[i]=0;
    ve.clear();
    ve.resize(2);

    for(int i=2,j;i<=nn;i++)
     for(j=2;j<=nn;j++)
      if(dis[i][j]<a&&i/2!=j/2) add(i,j^1);

    for(int i=2;i<=nn;i++) if(dfn[i]==0) tarjan(i);

    for(int i=1;i<=n;i++) if(id[i<<1]==id[(i<<1)^1]) return 0;
    return 1;
}
//.................................................
int main()
{
    scanf("%d",&n);
    nn=n*2+1;
    for(int i=1,a,b,c;i<=n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        w[i<<1].a=a,w[i<<1].b=b+c;
        w[(i<<1)^1].a=a,w[(i<<1)^1].b=b-c;
    }

    ll l=0,r,mid;

    for(int i=2,j;i<=nn;i++)
     for(j=i+1;j<=nn;j++) 
      dis[i][j]=dis[j][i]=dis_(w[i],w[j]),r=max(r,dis[i][j]); 

    while(l<=r)
    {
        mid=(l+r)>>1;
        if(judge(mid)) l=mid+1;
        else r=mid-1;
    }
    printf("%I64d",r);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值