题意:给定n个数对(xi,yi),从每个数对中选出一个数,使得得到的n个数之间的最小距离最大。求最大的最小距离。
分析:二分最大的最小距离mid。当选出某个数x后,对于数对(xi,yi),若|x-xi|<mid,则不能选xi,从而必须选yi。因此可以把问题转化为2-sat问题。如果暴力加边,那么最坏情况下会有O(n^2)条边,因此要对加边进行优化。注意到对每个数x,所加的边在某种意义是连续的(将2*n个数按其值从小到大排序之后),因此增加O(n)个虚拟顶点,用线段树维护加边,从而总边数变为O(nlgn)级别的。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5;
struct node
{
int x,i;
bool operator < (const node &o) const
{
return x<o.x;
}
}nd[maxn];
int n,mp[maxn];
int sz,lc[maxn],rc[maxn];
vector<int> G[maxn];
void init()
{
for (int i=0;i<maxn;i++) G[i].clear();
sz=2*n;
}
void add(int u,int v)
{
G[u].push_back(v);
}
void build(int l,int r)
{
int o=sz++;
if (l==r)
{
lc[o]=rc[o]=0;add(o,mp[nd[l].i^1]);
return ;
}
int mid=(l+r)/2;
lc[o]=sz;build(l,mid);
rc[o]=sz;build(mid+1,r);
add(o,lc[o]);add(o,rc[o]);
}
int get1(int x)
{
int l=0,r=2*n-1,mid,ret;
while (l<=r)
{
mid=(l+r)/2;
if (nd[mid].x>=x)
{
ret=mid;
r=mid-1;
}
else l=mid+1;
}
return ret;
}
int get2(int x)
{
int l=0,r=2*n-1,mid,ret;
while (l<=r)
{
mid=(l+r)/2;
if (nd[mid].x<=x)
{
ret=mid;
l=mid+1;
}
else r=mid-1;
}
return ret;
}
void work(int o,int l,int r,int ql,int qr,int p)
{
if (ql<=l&&r<=qr)
{
add(p,o);return ;
}
int mid=(l+r)/2;
if (ql<=mid) work(lc[o],l,mid,ql,qr,p);
if (qr>mid) work(rc[o],mid+1,r,ql,qr,p);
}
int dfs_clock,scc_cnt,sccno[maxn],pre[maxn],low[maxn];
stack<int> S;
void dfs(int u)
{
pre[u]=low[u]=++dfs_clock;
S.push(u);
for (int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if (!pre[v])
{
dfs(v);low[u]=min(low[u],low[v]);
}
else if (!sccno[v]) low[u]=min(low[u],pre[v]);
}
if (low[u]==pre[u])
{
scc_cnt++;
for (;;)
{
int x=S.top();S.pop();
sccno[x]=scc_cnt;
if (x==u) break;
}
}
}
void tarjan(int n)
{
dfs_clock=scc_cnt=0;
memset(sccno,0,sizeof(sccno));
memset(pre,0,sizeof(pre));
for (int i=0;i<n;i++) if (!pre[i]) dfs(i);
}
bool check(int mid)
{
init();
build(0,2*n-1);
for (int i=0;i<2*n;i++)
{
int i1=get1(nd[i].x-mid+1),i2=get2(nd[i].x+mid-1);
if (i1<i) work(2*n,0,2*n-1,i1,i-1,i);
if (i<i2) work(2*n,0,2*n-1,i+1,i2,i);
}
tarjan(sz);
for (int i=0;i<2*n;i++) if (sccno[i]==sccno[mp[nd[i].i^1]]) return 0;
return 1;
}
int main()
{
cin>>n;
for (int i=0;i<2*n;i++) scanf("%d",&nd[i].x),nd[i].i=i;
sort(nd,nd+2*n);
for (int i=0;i<2*n;i++) mp[nd[i].i]=i;
int l=1,r=1e9,mid,ans=0;
while (l<=r)
{
mid=(l+r)/2;
if (check(mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
cout<<ans;
return 0;
}