[UOJ356]Port Facility

相当于给定一些区间,我们要把这些区间分成两组,使得每组内的区间要么分离要么包含

如果两个区间相交且不包含,那么在它们之间连一条边(不能在同一个栈中),最后如果不是二分图就无解,否则设连通块个数为$cnt$,则答案为$2^{cnt}$(每个连通块确定一个点属于哪个栈,其他点全都确定了,所以一个连通块有两种方法)

暴力连边是$O(n^2)$的,我们要优化连边

因为最后我们要染色判二分图,所以不妨设$0$边为“经过这条边颜色不变”的边,$1$边为“经过这条边颜色改变”的边

先把所有区间按$l$排序,依次从区间$i$连边,同时用set以$r$为关键字存区间,那么因为当前在set里的区间左端点都比$i$的左端点小,set里右端点在$i$内的即是要连边的区间$j$

对于每个$j$,我们都要连边$(i,j,1)$,不妨连$(i,j_1,1)$和$(j_1,j_2,0)\cdots$,$0$边连了$j_\cdots$一条链

注意到我们会连很多无用$0$边,所以用另一个set以同样顺序维护区间,但这个set不存那些已经连了左右$0$边的区间,连边的时候只从这个set里找就可以了

然而时间复杂度我好像不会分析

有兴趣的话可以去看一下官方题解,解法②的分治还是很有参考价值的(把所有区间分成三类:被$[l,mid]$包含的,被$[mid+1,r]$包含的,包含$[mid,mid+1]$的,然后分治并处理第三类和每一类的交)

#include<stdio.h>
#include<stdlib.h>
#include<set>
using namespace std;
const int inf=2147483647;
int bl[2000010],h[1000010],nex[4000010],to[4000010],v[4000010],c[1000010],M;
void ins(int a,int b,int c){
	M++;
	to[M]=b;
	v[M]=c;
	nex[M]=h[a];
	h[a]=M;
}
void add(int a,int b,int c){
	ins(bl[a],bl[b],c);
	ins(bl[b],bl[a],c);
}
int r[2000010],st[1000010],pr[2000010],nx[2000010],tp;
set<int>s1,s2;
set<int>::iterator it;
void dfs(int x,int f){
	if(c[x]){
		if(c[x]!=f){
			putchar('0');
			exit(0);
		}
		return;
	}
	c[x]=f;
	for(int i=h[x];i;i=nex[i]){
		dfs(to[i],f^v[i]);
	}
}
int main(){
	int n,i,x,y,ans;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d%d",&x,&y);
		r[x]=y;
		bl[y]=i;
	}
	s1.insert(inf);
	s1.insert(-inf);
	s2.insert(inf);
	s2.insert(-inf);
	for(i=1;i<=2*n;i++){
		if(r[i]){
			it=s1.lower_bound(r[i]);
			nx[r[i]]=*it;
			it--;
			pr[r[i]]=*it;
			if(*it>i)add(r[i],*it,1);
			it=s2.lower_bound(r[i]);
			for(it--;*it>i;it--){
				if(pr[*it]>i){
					add(*it,pr[*it],0);
					pr[*it]=-inf;
				}
				if(nx[*it]<r[i]){
					add(*it,nx[*it],0);
					nx[*it]=inf;
				}
				if(pr[*it]==-inf&&nx[*it]==inf)st[++tp]=*it;
			}
			while(tp>0)s2.erase(st[tp--]);
			s1.insert(r[i]);
			s2.insert(r[i]);
		}
	}
	ans=1;
	for(i=1;i<=n;i++){
		if(!c[i]){
			dfs(i,2);
			(ans<<=1)%=1000000007;
		}
	}
	printf("%d",ans);
}

猜你喜欢

转载自www.cnblogs.com/jefflyy/p/9169149.html