【AtCoder Regular Contest 069 F】Flags
2-SAT,线段树优化建图,二分答案。
题目大意:
给定一个数轴和 $n$ 个点,每个点有两个备选坐标,要选一个作为其坐标。
要求找到最大的 $d$,使得存在一种安排点的坐标的方案,满足任意两点之间的距离大于等于 $d$。
题解:
一个点有两种选择,选择之间有约束条件。
可以想到使用 2-SAT 解决。
二分 $d$,然后有约束条件的点之间连边,然后跑 2-SAT 即可知道是否有合法解。
但是边数是 $O(n^2)$ 的,总复杂度高达 $O(n^2\log n)$,不能接受。
这里每个点约束到的点只能是左、右的连续区间,所以可以使用线段树优化建图。
总时间复杂度 $O(n\log^2 n)$。
Code:
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=2e5+6;
#define i0(x)(x*2)
#define i1(x)(x*2+1)
vector<int>G[N];
int n,X[N],Y[N],dfn[N],top,SCC,low[N],sta[N],idx,bel[N];
bool vis[N];
pair<int,int>A[N];
inline void addedge(int u,int v){G[u].push_back(v);}
void build(int l,int r,int o){
const int id=o+n;
if(l==r){
addedge(i0(id),A[l].second^1),addedge(A[l].second,i1(id));
}else{
const int mid=l+r>>1;
build(l,mid,o<<1),build(mid+1,r,o<<1|1);
const int idL=(o<<1)+n,idR=(o<<1|1)+n;
addedge(i0(id),i0(idL)),addedge(i1(idL),i1(id));
addedge(i0(id),i0(idR)),addedge(i1(idR),i1(id));
}
}
void add(int l,int r,int o,const int&L,const int&R,const int&id_){
if(L<=l&&r<=R){
const int id=o+n;
addedge(id_,i0(id)),addedge(i1(id),id_^1);
}else{
const int mid=l+r>>1;
if(L<=mid)add(l,mid,o<<1,L,R,id_);
if(mid<R)add(mid+1,r,o<<1|1,L,R,id_);
}
}
void dfs(int now){
vis[now]=1,sta[++top]=now;
dfn[now]=low[now]=++idx;
for(int to:G[now])
if(!dfn[to]){
dfs(to);
low[now]=min(low[now],low[to]);
}else if(vis[to])low[now]=min(low[now],dfn[to]);
if(low[now]==dfn[now]){
++SCC;
int v;
do{
v=sta[top--];
vis[v]=0;
bel[v]=SCC;
}while(v!=now);
}
}
bool check(int d){
for(int i=0;i<N;++i)G[i].clear();
build(0,2*n-1,1);
for(int i=0;i<2*n;++i){
int it=lower_bound(A,A+2*n,make_pair(A[i].first-d+1,0))-A;
if(it<i)add(0,2*n-1,1,it,i-1,A[i].second);
it=lower_bound(A,A+2*n,make_pair(A[i].first+d,0))-A-1;
if(i<it)add(0,2*n-1,1,i+1,it,A[i].second);
}
memset(dfn,0,sizeof dfn);
memset(bel,0,sizeof bel);
idx=SCC=top=0;
for(int i=0;i<N;++i)if(!dfn[i])dfs(i);
for(int i=0;i<N/2;++i)
if(bel[i0(i)]==bel[i1(i)])return 0;
return 1;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=0;i<n;++i)cin>>X[i]>>Y[i],A[i0(i)]=make_pair(X[i],i0(i)),A[i1(i)]=make_pair(Y[i],i1(i));
sort(A,A+2*n);
int l=1,r=A[2*n-1].first-A[0].first,ans=0;
while(l<=r){
const int mid=l+r>>1;
if(check(mid))l=(ans=mid)+1;else r=mid-1;
}
printf("%d\n",ans);
return 0;
}