D46 2-SAT+线段树优化+二分 [ARC069F] Flags

视频链接:D46 2-SAT+线段树优化+二分 [ARC069F] Flags_哔哩哔哩_bilibili

 

 

 

 

[ARC069F] Flags - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

复制代码
// 2-SAT+线段树优化+二分 O(nlognlogx)
#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;
int n,tot,id[M];
struct F{
  int x,id; //坐标,编号
  F(int x=0):x(x){}
  bool operator<(const F& b)const{return x<b.x;}
}f[N]; //旗子

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);
}
#undef mid

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 mid){
  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].x-mid))-f;
    y=lower_bound(f+1,f+1+2*n,F(f[i].x+mid))-f-1;
    link(1,1,2*n,x,i-1,f[i].id); //x是距离<mid的左下标
    link(1,1,2*n,i+1,y,f[i].id); //y是距离<mid的右下标
  }
  
  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].x,&f[i+n].x);
    f[i].id=i,f[i+n].id=i+n; //点的编号
  }
  sort(f+1,f+n*2+1); //按x排序
  int l=0,r=f[2*n].x-f[1].x+1,mid;
  while(l+1<r){ //二分距离
    mid=(l+r)/2;
    check(mid)?l=mid:r=mid;
  }
  printf("%d",l);
}
复制代码

 

UVA1146 Now or later - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

复制代码
// 2-SAT+线段树优化+二分 O(nlognlogx)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

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

const int N=4010,M=N<<2;
vector<int> v[M]; //邻接表
int dfn[M],low[M],stk[M],scc[M],tim,top,sc;
int n,tot,id[M];
struct F{
  int x,id; //坐标,编号
  F(int x=0):x(x){}
  bool operator<(const F& b)const{return x<b.x;}
}f[N]; //旗子

void build(int u,int l,int r){
  id[u]=++tot; //节点编号
  if(l==r){
    int x=f[l].id;
    v[id[u]].push_back(x<=n?x+n:x-n); //叶子向反点连边
    return;
  }
  build(ls,l,mid);
  build(rs,mid+1,r);
  v[id[u]].push_back(id[ls]);
  v[id[u]].push_back(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){
    v[p].push_back(id[u]); //p点向区间id[u]连边
    return;
  }
  link(ls,l,mid,x,y,p),
  link(rs,mid+1,r,x,y,p);
}
#undef mid

void tarjan(int x){
  dfn[x]=low[x]=++tim;
  stk[++top]=x;
  for(int y:v[x]){
    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 mid){
  for(int i=1;i<=8*n;++i) v[i].clear();
  memset(dfn,0,sizeof(dfn));
  memset(low,0,sizeof(low));
  memset(scc,0,sizeof(scc));
  memset(stk,0,sizeof(stk));
  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].x-mid))-f;
    y=lower_bound(f+1,f+1+2*n,F(f[i].x+mid))-f-1;
    link(1,1,2*n,x,i-1,f[i].id); //x是距离<mid的左下标
    link(1,1,2*n,i+1,y,f[i].id); //y是距离<mid的右下标
  }
  
  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(){
  while(scanf("%d",&n)!=EOF){
    for(int i=1;i<=n;i++){
      scanf("%d%d",&f[i].x,&f[i+n].x);
      f[i].id=i,f[i+n].id=i+n; //点的编号
    }
    sort(f+1,f+n*2+1); //按x排序
    int l=0,r=1e7+1,mid;
    while(l+1<r){ //二分距离
      mid=(l+r)>>1;
      check(mid)?l=mid:r=mid;
    }
    printf("%d\n",l);
  }
}
复制代码

 

posted @   董晓  阅读(209)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2023-08-18 C27 线段树 区间最大公约数
2023-08-18 C26 线段树 区间最大子段和
点击右上角即可分享
微信分享提示