【JZOJ5446】高考是不可能高考的

###Description
Snuke 喜欢旗子.
Snuke 正在将N 个旗子摆在一条线上.
第i 个旗子可以被放在位置xi 或yi 上.
Snuke 认为两个旗子间的最小距离越大越好. 请你求出最大值.

###Solution
如题,打工是不可能打工的,高考是不可能高考的
看到最小距离最大直接二分答案,考虑怎么判定。

我们先限制一下点对(一个旗子能放的两个位置)的关系(后面说怎么限制)。

然后把所有位置打散,映射到数轴上,对于一个位置 i i i放置旗子,那么区间 [ i − m i d + 1 , i + m i d − 1 ] [i-mid+1,i+mid-1] [imid+1,i+mid1]中所有能放旗子的别的位置都不能放。那么这就是一个经典的2-sat问题。同时点对的限制也是一个2-sat限制关系。

但这题暴力连边可能会T,我们发现一个位置i会向连续的区间连边,于是我们可以对区间建棵线段树,连的时候直接连向对应区间即可。这样连的边数是 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)条的。

###Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
#define N 10010
#define M 100010
#define inf 2147483647
#define abs(x) ((x)<0?-(x):(x))
using namespace std;
int n;
struct node{
	int x,y;
}a[N],b[N*2];
int tot=0;
bool cmp(node x,node y){
	return x.x<y.x;
}
int low[M],dfn[M],in[M];
int st[M],tt=0;
int col=0,c[M];
int to[N*100],nx[N*100],ls[M],num=0;
void link(int x,int y){
	to[++num]=y,nx[num]=ls[x],ls[x]=num;
}
void tarjan(int x){
	low[x]=dfn[x]=++tt,in[st[tt]=x]=true;
	rep(i,x){
		int v=to[i];
		if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
		else if(in[v]) low[x]=min(low[x],dfn[v]);
	}
	if(low[x]==dfn[x]){
		col++;
		while(st[tt+1]!=x && tt){
			int o=st[tt--];
			c[o]=col,in[o]=false;
		}
	}
}
void clear(){
	memset(c,0,sizeof(c)),col=0;
	memset(low,0,sizeof(low));
	memset(dfn,0,sizeof(dfn));
	memset(st,0,sizeof(st));
	memset(ls,0,sizeof(ls)),num=0;
}
int nw;
int ans=0;
int jy[N],ne[N*2];
int tr[N*8];
void build(int v,int l,int r){
	if(l==r) {tr[v]=l+tot;return;}
	int mid=(l+r)>>1;
	build(v<<1,l,mid),build((v<<1)+1,mid+1,r);
	tr[v]=++nw;
	link(tr[v],tr[v<<1]),link(tr[v],tr[(v<<1)+1]);
}
void lk(int v,int l,int r,int x,int y,int t){
	if(x>y) return;
	if(l==x && r==y){
		link(t,tr[v]);
		return;
	}
	int mid=(l+r)>>1;
	if(y<=mid) lk(v<<1,l,mid,x,y,t);
	else if(x>mid) lk((v<<1)+1,mid+1,r,x,y,t);
	else lk(v<<1,l,mid,x,mid,t),lk((v<<1)+1,mid+1,r,mid+1,y,t);
}
bool check(int mid){
	clear();
	fo(i,1,tot)
	if(ne[i]) link(i,ne[i]+tot),link(ne[i]+tot,i),link(i+tot,ne[i]),link(ne[i],i+tot);
	nw=tot*2,build(1,1,tot);
	int l=1,r=1;
	fo(i,1,tot)
	{
		int wz=b[i].x;
		while(r<tot && wz+mid>b[r+1].x) r++;
		while(wz-mid>=b[l].x) l++;
		lk(1,1,tot,l,i-1,i),lk(1,1,tot,i+1,r,i);
		//fo(j,l,r) if(i!=j) link(i,j+n);
	}
	fo(i,1,tot)
	if(!dfn[i]) tarjan(i);
	fo(i,1,tot) if(c[i]==c[i+tot] && c[i]) return false;
	return true;
}
int main()
{
	scanf("%d",&n);
	fo(i,1,n){
		scanf("%d %d",&a[i].x,&a[i].y);
		if(a[i].x>a[i].y) swap(a[i].x,a[i].y);
		b[++tot].x=a[i].x,b[tot].y=i;
		b[++tot].x=a[i].y,b[tot].y=i;
	}
	sort(b+1,b+tot+1,cmp);
	fo(i,1,tot){
		if(jy[b[i].y]) ne[jy[b[i].y]]=i;
		jy[b[i].y]=i;
	}
	int l=0,r=(b[tot].x-b[1].x)/(n-1)+1;
	while(l+1<r){
		int mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	if(check(r)) l=r;
	printf("%d",l);
}
posted @ 2017-11-03 22:30  sadstone  阅读(40)  评论(0编辑  收藏  举报