AT2336 [ARC069D] Flags 2-SAT 线段树优化建图

题意:

戳这里

分析:

挺裸的一道题,直接二分答案,然后建图跑 \(2-SAT\) 判断合法性

但是会发现复杂度瓶颈在于 \(O(n^2)\) 建图

我们发现每一次连边都是向距离在 \((min,pos_i-mid)\)\((pos_i+mid,max)\) 这段区间内的点连边,然后我们就可以用线段树优化建图来将建图的复杂度降低到 \(O(n\log )\)

tip:

  1. 按照 \(pos\) 为关键字对点进行升序排序,建图的时候,每个叶子结点向自己的对应点连一条边,这样连边方式变成了向一段区间内连边,但是一定要少掉自己,即向 \((pos_i-mid,pos_i)\)\((pos_i+1,pos_i+mid)\) 区间连边
  2. 由于每次 \(check\) 会清空 \(head\) 数组,所以会将建出来的线段树也清掉,所以每次记得重建一次

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
#define inl inline
#define reg register

using namespace std;

namespace zzc
{
	inline int read()
	{
		int x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 2e5+5;
	int n,m,top,col,tim,idx,cnt;
	int id[maxn],head[maxn],bel[maxn],dfn[maxn],low[maxn],st[maxn];
	bool vis[maxn];
	
	struct edge
	{
		int to,nxt;
	}e[maxn<<3];
	
	void add(int u,int v)
	{
		e[++cnt].to=v;
		e[cnt].nxt=head[u];
		head[u]=cnt;
	}
	
	struct node
	{
		int pos,id;
		node(){}
		node(int pos,int id=0):pos(pos),id(id){}
		bool operator <(const node &b)const
		{
			return pos<b.pos;
		}
	}p[maxn];
	
	int op(int x)
	{
		return x<=n?x+n:x-n;
	}
	
	void build(int rt,int l,int r)
	{
		id[rt]=++idx;
		if(l==r)
		{
			add(id[rt],op(p[l].id));
			return ;
		}
		int mid=(l+r)>>1;
		build(lc,l,mid);build(rc,mid+1,r);
		add(id[rt],id[lc]);add(id[rt],id[rc]);
	}
	
	void link(int rt,int l,int r,int ql,int qr,int x)
	{
		if(ql>qr) return ;
		if(ql<=l&&r<=qr)
		{
			add(x,id[rt]);
			return ;
		}
		int mid=(l+r)>>1;
		if(ql<=mid) link(lc,l,mid,ql,qr,x);
		if(qr>mid) link(rc,mid+1,r,ql,qr,x);
	}
	
	void tarjan(int u)
	{
		dfn[u]=low[u]=++tim;
		st[++top]=u;
		vis[u]=true;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(!dfn[v])
			{
				tarjan(v);
				low[u]=min(low[u],low[v]);
			}
			else if(vis[v]) low[u]=min(low[u],dfn[v]);
		}
		if(low[u]==dfn[u])
		{
			col++;
			do
			{
				bel[st[top]]=col;
				vis[st[top]]=false;
			}while(st[top--]!=u);
		}
	}
	
	bool check(int x)
	{
		for(int i=1;i<=idx;i++) head[i]=0,dfn[i]=0;
		cnt=0;tim=0;top=0;
		build(1,1,idx=2*n);
		for(int i=1;i<=2*n;i++)
		{
			int l=upper_bound(p+1,p+2*n+1,node(p[i].pos-x))-p;
			int r=upper_bound(p+1,p+2*n+1,node(p[i].pos+x-1))-p-1;
			link(1,1,2*n,l,i-1,p[i].id);link(1,1,2*n,i+1,r,p[i].id);
		}
		for(int i=1;i<=2*n;i++) if(!dfn[i]) tarjan(i);
		for(int i=1;i<=n;i++) if(bel[i]==bel[i+n]) return false;
		return true;
	}
	
	void work()
	{
		n=read();
		for(int i=1;i<=n;i++)
		{
			p[i].pos=read();p[i+n].pos=read();
			p[i].id=i;p[i+n].id=i+n;
		}
		sort(p+1,p+2*n+1);
		int l=0,r=p[2*n].pos-p[1].pos+1,mid,ans;
		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()
{
	zzc::work();
	return 0;
}

posted @ 2021-01-16 08:21  youth518  阅读(77)  评论(0编辑  收藏  举报