【arc069F】Flags

<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();
}
相關文章
相關標籤/搜索