D46 2-SAT+線段樹最佳化+二分 [ARC069F] Flags

董晓發表於2024-08-18

影片連結:

[ARC069F] Flags - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)

// D46 2-SAT+線段樹最佳化+二分 O(nlognlogv)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

#define mid ((l+r)>>1)
#define ls (u<<1)
#define rs (u<<1|1)

const int N=20010,M=N*10;
int head[M],ne[N*40],to[N*40],idx;
void add(int x,int y){
  to[++idx]=y;ne[idx]=head[x];head[x]=idx;
}
int dfn[M],low[M],stk[M],scc[M],tim,top,sc;
struct F{
  int pos,id;
  bool operator<(const F& b)const{return pos<b.pos;}
  F(int pos=0):pos(pos){}
}f[N];
int n,tot,id[M];

void build(int u,int l,int r){
  id[u]=++tot; //節點編號
  if(l==r){
    int x=f[l].id;
    add(id[u],x<=n?x+n:x-n); //葉子→反點
    return;
  }
  build(ls,l,mid);
  build(rs,mid+1,r);
  add(id[u],id[ls]);
  add(id[u],id[rs]); //父→子
}
void link(int u,int l,int r,int x,int y,int p){
  if(x>r||y<l) return;
  if(x<=l&&r<=y){
    add(p,id[u]); //p點向區間id[u]連邊
    return;
  }
  link(ls,l,mid,x,y,p),
  link(rs,mid+1,r,x,y,p);
}
void tarjan(int x){
  dfn[x]=low[x]=++tim;
  stk[++top]=x;
  for(int i=head[x];i;i=ne[i]){
    int y=to[i];
    if(!dfn[y]){ //若y尚未訪問
      tarjan(y);
      low[x]=min(low[x],low[y]);
    }
    else if(!scc[y]) //若y已訪問且未處理
      low[x]=min(low[x],dfn[y]);
  }
  if(low[x]==dfn[x]){ //若x是SCC的根
    ++sc;
    for(int y=-1;y!=x;)
      scc[y=stk[top--]]=sc;
  }
}
bool check(int m){
  memset(head,0,sizeof(head));
  memset(dfn,0,sizeof(dfn));
  memset(low,0,sizeof(low));
  memset(scc,0,sizeof(scc));
  idx=tim=top=sc=0;
  
  build(1,1,tot=2*n);
  for(int i=1,x,y;i<=2*n;i++){
    x=upper_bound(f+1,f+1+2*n,F(f[i].pos-m))-f;
    y=upper_bound(f+1,f+1+2*n,F(f[i].pos+m-1))-f-1;
    link(1,1,2*n,x,i-1,f[i].id); //x是距離<m的左下標
    link(1,1,2*n,i+1,y,f[i].id); //y是距離<m的右下標
  }
  
  for(int i=1;i<=2*n;i++) if(!dfn[i]) tarjan(i);
  for(int i=1;i<=n;i++) if(scc[i]==scc[i+n]) return 0;
  return 1;
}
int main(){
  scanf("%d",&n);
  for(int i=1;i<=n;i++){
    scanf("%d%d",&f[i].pos,&f[i+n].pos);
    f[i].id=i,f[i+n].id=i+n; //
  }
  sort(f+1,f+n*2+1); //按pos排序
  int l=0,r=f[2*n].pos-f[1].pos+1,m;
  while(l+1<r){ //二分距離
    m=(l+r)/2;
    check(m)?l=m:r=m;
  }
  printf("%d",l);
}