Flags

题目链接:Flags


首先这个可以二分。

然后这个东西可以 2-SAT,然后我们可以发现我们对每个点连边的时候连的是一个区间。所以我们可以用线段树优化。

如果对每个点都建立选 or 不选两个点的话,我们就需要两颗线段树了。我们可以优化一下,对于一个点选的话,我们找到这个区间不能选的点,然后就是连向这个区间所有点的另一个点,也就是选这个点,那么必须选这个区间的另一个点。

然后找区间的时候,不能连向自己的反点。


AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
//#define int long long
using namespace std;
const int N=1e5+10,M=N*20;
int n,a[N][2],id[M],cnt;
int vis[M],dfn[M],scc[M],low[M],idx,co; stack<int> s;
vector<int> g[M]; vector<pair<int,int> > v;
#define mid (l+r>>1)
void build(int p,int l,int r){
	if(l==r){id[p]=v[l-1].second; return ;}
	id[p]=++cnt;
	build(p<<1,l,mid),build(p<<1|1,mid+1,r);
	g[id[p]].push_back(id[p<<1]),g[id[p]].push_back(id[p<<1|1]);
}
void link(int p,int l,int r,int ql,int qr,int v){
	if(l==ql&&r==qr){g[v].push_back(id[p]); return ;}
	if(qr<=mid) link(p<<1,l,mid,ql,qr,v);
	else if(ql>mid) link(p<<1|1,mid+1,r,ql,qr,v);
	else link(p<<1,l,mid,ql,mid,v),link(p<<1|1,mid+1,r,mid+1,qr,v);
}
#undef mid
void Tarjan(int x){
	dfn[x]=low[x]=++idx; vis[x]=1; s.push(x);
	for(int to:g[x]){
		if(!dfn[to]) Tarjan(to),low[x]=min(low[x],low[to]);
		else if(vis[to]) low[x]=min(low[x],dfn[to]);
	}
	if(dfn[x]==low[x]){
		int u; co++;
		do{
			u=s.top(); s.pop(); vis[u]=0; scc[u]=co;
		}while(u!=x);
	}
}
inline int check(int mid){
	cnt=2*n; build(1,1,2*n);
	for(int i=1;i<=n;i++) for(int j=0;j<2;j++){
		int l=lower_bound(v.begin(),v.end(),make_pair(a[i][j]-mid+1,0))-v.begin()+1;
		int r=upper_bound(v.begin(),v.end(),make_pair(a[i][j]+mid,0))-v.begin();
		int m=lower_bound(v.begin(),v.end(),make_pair(a[i][j],i+(j^1)*n))-v.begin()+1;
		if(l<m)	link(1,1,2*n,l,m-1,i+j*n);
		if(m<r) link(1,1,2*n,m+1,r,i+j*n);
	}
	while(s.size())	s.pop();
	for(int i=1;i<=cnt;i++) dfn[i]=low[i]=scc[i]=0; idx=co=0;
	for(int i=1;i<=cnt;i++) if(!dfn[i]) Tarjan(i);
	for(int i=1;i<=cnt;i++) g[i].clear();
	for(int i=1;i<=n;i++) if(scc[i]==scc[i+n]) return 0;
	return 1;
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i][0]>>a[i][1];
	for(int i=1;i<=n;i++) v.push_back({a[i][0],i+n}),v.push_back({a[i][1],i});
	sort(v.begin(),v.end());
	int l=0,r=1e9;
	while(l<r){
		int mid=l+r+1>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l;
	return 0;
}

你可能感兴趣的:(图论,2-SAT,线段树)