相当于给定一些区间,我们要把这些区间分成两组,使得每组内的区间要么分离要么包含
如果两个区间相交且不包含,那么在它们之间连一条边(不能在同一个栈中),最后如果不是二分图就无解,否则设连通块个数为$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); }