jzoj【NOIP2017提高A组冲刺11.3】高考是不可能高考的

题目

Time Limits: 2000 ms Memory Limits: 1048576 KB
Description

Snuke 喜欢旗子.
Snuke 正在将N 个旗子摆在一条线上.
第i 个旗子可以被放在位置xi 或yi 上.
Snuke 认为两个旗子间的最小距离越大越好. 请你求出最大值.

Input

N
x1 y1

xN yN

Output

输出一行一个整数表示答案.

Sample Input

输入1:
3
1 3
2 5
1 9
输入2:
5
2 2
2 2
2 2
2 2
2 2
输入3:
22
93 6440
78 6647
862 11
8306 9689
798 99
801 521
188 206
6079 971
4559 209
50 94
92 6270
5403 560
803 83
1855 99
42 504
75 484
629 11
92 122
3359 37
28 16
648 14
11 269

Sample Output

输出1:
4
输出2:
0
输出3:
17

Data Constraint

对于30% 的数据, N <=18;
对于60% 的数据, N <=1000;
对于100% 的数据, 1 <= N <= 10^4; 1 <= xi, yi <= 10^9.

题解

2-set的连边:如果选了一个点后不能选另一个点,那么就在那个点对应的另一个点连一条单向边
考虑2-set
一个比较显然的思路是我们可以二分答案,然后用2-set判断可行性
但是这样会超时
我们可以先把所有的点排序,然后就可以比较快的连边了,我们还可以事先搞一个较小的上界,也就是对两个点对中最远的点对的距离的最小值

但是这只是一个水法

我们有一个很优秀的算法
我们可以对a,b分别排序,然后建两颗线段树,然后对于一个点,它可以连的边就是线段树中对应的连续的一段点,那么我们不妨连到那个子树的根,对线段树我们从上往下连一些单向边就好了
这样我们就可以用n log n级别的点数和边树完成对图的构造了,然后我们对这个图跑一下2-set就好了

贴代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)

using namespace std;

const int maxn=2e4+5,cs=3e7+5;

struct P{
    int x,y;
}a[maxn];
int fi[maxn*2],ne[cs*2],dui[cs*2],qc[cs*2];
bool bb[maxn*2],bz[maxn*2];
int dfn[maxn],low[maxn],st[maxn],cc[maxn];
int i,j,k,l,r,n,m,x,y,ma,mid,nc,cq,tp,now;

int cmp(P x,P y){
    return x.x<y.x;
}
void add(int x,int y){
    if (fi[x]==0) fi[x]=++now; else ne[qc[x]]=++now;
    qc[x]=now; dui[now]=y;
}
void ge_bian(){
    fo(i,1,now) ne[i]=0;
    now=0;
    memset(fi,0,sizeof(fi));
    fo(i,1,2*n){
        j=i-1;
        while (j>0 && a[i].x-a[j].x<mid){
            if (abs(a[i].y-a[j].y)!=n){
                if (a[j].y>n) add(a[i].y,a[j].y-n); else add(a[i].y,a[j].y+n);
            }
            j--;
        }
        j=i+1;
        while (j<=2*n && a[j].x-a[i].x<mid){
            if (abs(a[i].y-a[j].y)!=n){
                if (a[j].y>n) add(a[i].y,a[j].y-n); else add(a[i].y,a[j].y+n);
            }
            j++;
        }
    }
}
void tarjan(int x){
    dfn[x]=low[x]=++cq; bz[x]=true; bb[x]=true;
    st[++tp]=x;
    int i=fi[x];
    while (i){
        if (bz[dui[i]]==false){
            tarjan(dui[i]);
            low[x]=min(low[x],low[dui[i]]);
        } else if (bb[dui[i]]==true) low[x]=min(low[x],low[dui[i]]);
        i=ne[i];
    }
    if (dfn[x]==low[x]){
        nc++;
        while (true){
            cc[st[tp]]=nc;
            bb[st[tp]]=false;
            tp--;
            if (st[tp+1]==x) break;
        }
    }
}
bool check(){
    memset(bz,false,sizeof(bz));
    memset(bb,false,sizeof(bb));
    memset(cc,0,sizeof(cc));
    nc=cq=tp=0;
    fo(i,1,2*n) 
        if (bz[i]==false) tarjan(i);
    fo(i,1,n) if (cc[i]==cc[i+n]) return false;
    return true;
}
int main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&n);
    ma=0x7fffffff;
    fo(i,1,n){
        scanf("%d%d",&x,&y);
        a[i].x=x; a[i].y=i;
        a[i+n].x=y; a[i+n].y=i+n;
        fo(j,1,i-1){
            x=max(abs(a[i].x-a[j].x),abs(a[i].x-a[j+n].x));
            y=max(abs(a[i+n].x-a[j].x),abs(a[i+n].x-a[j+n].x));
            ma=min(ma,max(x,y));
        }
    }
    sort(a+1,a+n*2+1,cmp);
    fo(i,1,2*n) dui[a[i].y]=i;
    l=1;
    r=ma;
    while (l<r){
        mid=(l+r)/2;
        ge_bian();
        if (check()) l=mid+1; else r=mid;
    }
    mid=l;
    ge_bian(); if (check()==false) l--;
    printf("%d\n",l);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值