<font size=3>Portal -->arc069_fios
Solution
对于。。2-sat运用十分熟练的人来讲。。这题多是一眼2-sat Q^Q然而这并非我十分忧伤qwq数组
首先题目里面有一个很明显的暗示:能够放在$x_i$或$y_i$优化
那咱们能够将$x_i$和$y_i$当作两种不能共存的选择,咱们用一条$(u,v)$的有向边表示选了$u$就必须选$v$,以上都是2-sat的常规操做那么接下来咱们应该怎么办呢ui
若是说咱们如今的问题变成判断一个数是否多是最小值的话,这个问题能够采用这样的解决方式:咱们考虑那些必定要选的边,而后用它们无解来排除整个问题无解的状况url
对于一个点$i$,若是说咱们选了这个点的$x_i$,那么其余全部的$|x_j-x_i|<d$的点都不能选$x_j$而必须选$y_j$(不然$d$就不是最小了),对于选$y_i$的状况同理,咱们能够根据这个建出一个图,而后就能够继续2-sat常规操做大力tarjan缩点判断一下是否有解就行了,具体的话就是一个强联通份量中的全部点的选择状态必须一致,因此若是说存在一个$i$知足$x_i$和$y_i$在同一个强联通份量中那么就无解了spa
将问题转化为判断能够直接上个二分答案.net
如今的问题是怎么优化建图code
咱们将全部的$x$和$y$丢到一个数组(记为$lis$)里面排个序,能够发现知足$|x_j-x_i|<d$的$j$确定是连续的一段($l$和$r$能够直接用lower_bound和upper_bound查加一减一什么的有点烦==搞了很久),因此咱们能够考虑线段树优化建图,把边数控制在$nlogn$内,线段树就按照$lis$中的这个顺序来建,而后内部是从根到叶子连边,叶子连对应的点的。。反选点(是$x$就选$y$,是$y$就选$x$)就行了get
这里有一个小问题,点多是同样的,那咱们排完序以后不要去重,而后照常编号什么的就行了string
代码大概长这个样子
#include<iostream> #include<cstdio> #include<cstring> #include<map> #include<algorithm> using namespace std; const int N=1e4+10,SEG=N*4,M=N*100,N1=SEG+N*2; struct xxx{ int y,nxt; }a[M]; map<int,int>Id; int p[N][2],lis[N*2],loc[N*2],V[N*2]; int dfn[N1],low[N1],h[N1],st[N1],inst[N1],num[N1]; int n,m,tot,dfn_t,top,cnt,mx,mn,all,tmptot; void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;} namespace Seg{/*{{{*/ int ch[SEG][2],id[SEG]; int n,tot,addtg; void _build(int x,int l,int r){ if (l==r){ add(addtg+x,loc[l]); return; } int mid=l+r>>1; ch[x][0]=++tot; _build(ch[x][0],l,mid); ch[x][1]=++tot; _build(ch[x][1],mid+1,r); add(addtg+x,addtg+ch[x][0]); add(addtg+x,addtg+ch[x][1]); } void build(int _n){n=_n;tot=1;addtg=_n;_build(1,1,n);} void _link(int x,int l,int r,int lx,int rx,int from){ if (l<=lx&&rx<=r){ add(from,addtg+x); return; } int mid=lx+rx>>1; if (r<=mid) _link(ch[x][0],l,r,lx,mid,from); else if (l>mid) _link(ch[x][1],l,r,mid+1,rx,from); else{ _link(ch[x][0],l,mid,lx,mid,from); _link(ch[x][1],mid+1,r,mid+1,rx,from); } } void link(int l,int r,int from){if (l>r) return;_link(1,l,r,1,n,from);} }/*}}}*/ void prework(){ lis[0]=0; for (int i=1;i<=n;++i) lis[++lis[0]]=p[i][0],lis[++lis[0]]=p[i][1]; sort(lis+1,lis+1+lis[0]); mx=lis[lis[0]]; mn=lis[1]; //don't unique! int tmp; Id.clear(); Id[lis[1]]=1; for (int i=2;i<=lis[0];++i) if (lis[i-1]!=lis[i]) Id[lis[i]]=i; for (int i=1;i<=n;++i){ tmp=p[i][0]; p[i][0]=Id[p[i][0]]; V[Id[tmp]]=tmp; ++Id[tmp]; tmp=p[i][1]; p[i][1]=Id[p[i][1]]; V[Id[tmp]]=tmp; ++Id[tmp]; loc[p[i][0]]=n+i; loc[p[i][1]]=i; } } void tarjan(int x){ int u; low[x]=dfn[x]=++dfn_t; st[++top]=x; inst[x]=true; for (int i=h[x];i!=-1;i=a[i].nxt){ u=a[i].y; if (!dfn[u]){ tarjan(u); low[x]=min(low[x],low[u]); } else if (inst[u]) low[x]=min(low[x],dfn[u]); } if (low[x]==dfn[x]){ ++cnt; u=st[top]; while (u!=x){ inst[u]=false; num[u]=cnt; u=st[--top]; } inst[x]=false; num[x]=cnt; --top; } } void reset(){ memset(dfn,0,sizeof(dfn)); memset(h,-1,sizeof(h)); memset(inst,false,sizeof(inst)); tot=0; cnt=0; top=0; } void build(int d){ int l,r; Seg::build(lis[0]); for (int i=1;i<=n;++i){ l=lower_bound(lis+1,lis+1+lis[0],V[p[i][0]]-d+1)-lis; r=upper_bound(lis+1,lis+1+lis[0],V[p[i][0]]+d-1)-lis-1; if (l<=r){ Seg::link(l,p[i][0]-1,i); Seg::link(p[i][0]+1,r,i); } l=lower_bound(lis+1,lis+1+lis[0],V[p[i][1]]-d+1)-lis; r=upper_bound(lis+1,lis+1+lis[0],V[p[i][1]]+d-1)-lis-1; if (l<=r){ Seg::link(l,p[i][1]-1,i+n); Seg::link(p[i][1]+1,r,i+n); } } } bool check(int d){ reset(); build(d); all=Seg::tot+lis[0]; for (int i=1;i<=all;++i) if (!dfn[i]) tarjan(i); for (int i=1;i<=n;++i) if (num[i]==num[i+n]) return false; return true; } void solve(){ int l=0,r=mx-mn,mid,ans=l; while (l<=r){ mid=l+r>>1; if (check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%d\n",ans); } int main(){ #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); #endif scanf("%d",&n); for (int i=1;i<=n;++i) scanf("%d%d",&p[i][0],&p[i][1]); prework(); solve(); }