题目描述
放假啦!
小林和康娜来到了港口,看到有货船正在卸货。
港口十分狭窄,只有两个卸货区可以使用。每个卸货区上面可以堆积任意多个箱子。
每卸下来一个箱子,工作人员都会把这个箱子放在某个卸货区的顶端。之后,当车辆来运走这个箱子的时候,也必须保证这个箱子在某个卸货区的顶端。
港口今天一共运来了N个箱子,第i个箱子在时刻Ai被卸货,在时刻Bi被取走。康娜发现,每个箱子被取走时,都恰好位于所在卸货区的顶端。
康娜觉得很有意思,她想要知道,有多少种卸货方案,使得每个箱子在被取走时都位于所在卸货区的顶端呢?
两种方案不同,当且仅当存在一个箱子,使得在两个方案中这个箱子被放在了不同的卸货区。
做法
我们注意到这就是若干个区间。
求分成两组使得每组内互不相交的方案数统计。
如果相交的区间连边的话,不是二分图就无解。
方案数是2^联通块个数。
先不考虑无解判断,我们可以搜索出每一个联通块。
即枚举一个区间,如果没搜索过,找到所有未被搜索过的与它相交的区间搜索。
可以用线段树来不断找到与一个区间相交的区间。
在这个过程中我们可以顺便进行并查集染色。
如果两个同色的区间相交,显然就无解。
最后在用线段树做两次即可。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10,mo=1000000007;
int mi[maxn*8],mx[maxn*8],a[maxn],b[maxn],p[maxn*2],q[maxn*2];
int s1[maxn],s2[maxn],fa[maxn],co[maxn],dl[maxn];
bool bz[maxn];
int i,j,k,l,t,n,m,tot,top,ans,head,tail,now;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void changemi(int p,int l,int r,int a,int b){
if (l==r){
mi[p]=b;
return;
}
int mid=(l+r)/2;
if (a<=mid) changemi(p*2,l,mid,a,b);else changemi(p*2+1,mid+1,r,a,b);
mi[p]=min(mi[p*2],mi[p*2+1]);
}
void changemx(int p,int l,int r,int a,int b){
if (l==r){
mx[p]=b;
return;
}
int mid=(l+r)/2;
if (a<=mid) changemx(p*2,l,mid,a,b);else changemx(p*2+1,mid+1,r,a,b);
mx[p]=max(mx[p*2],mx[p*2+1]);
}
int querymi(int p,int l,int r,int a,int b){
if (a>b) return 2*n+1;
if (l==a&&r==b) return mi[p];
int mid=(l+r)/2;
if (b<=mid) return querymi(p*2,l,mid,a,b);
else if (a>mid) return querymi(p*2+1,mid+1,r,a,b);
else return min(querymi(p*2,l,mid,a,mid),querymi(p*2+1,mid+1,r,mid+1,b));
}
int querymx(int p,int l,int r,int a,int b){
if (a>b) return 0;
if (l==a&&r==b) return mx[p];
int mid=(l+r)/2;
if (b<=mid) return querymx(p*2,l,mid,a,b);
else if (a>mid) return querymx(p*2+1,mid+1,r,a,b);
else return max(querymx(p*2,l,mid,a,mid),querymx(p*2+1,mid+1,r,mid+1,b));
}
int getfa(int x){
if (!fa[x]) return x;
int t=getfa(fa[x]);
co[x]=(co[x]+co[fa[x]]+2)%2;
return fa[x]=t;
}
void merge(int x,int y){
int a=getfa(x),b=getfa(y);
if (a!=b){
(co[a]+=co[y]-co[x]+3)%=2;
fa[a]=b;
}
else if (a==b&&co[x]==co[y]) ans=0;
}
int main(){
freopen("port.in","r",stdin);freopen("port.out","w",stdout);
n=read();
fo(i,1,n) a[i]=read(),b[i]=read(),p[a[i]]=i;
ans=1;
/*fo(i,1,2*n){
if (p[i]==0){
if (tot&&b[s1[tot]]==i) tot--;
else top--;
}
else{
if (!tot||b[p[i]]<b[s1[tot]]) s1[++tot]=p[i];
else if (!top||b[p[i]]<b[s2[top]]) s2[++top]=p[i];
else{
ans=0;
break;
}
}
}
if (ans==0){
printf("0\n");
return 0;
}*/
fo(i,1,2*n) p[i]=q[i]=0;
fo(i,1,n) p[b[i]]=i,q[a[i]]=i;
fo(i,1,8*n) mi[i]=2*n+1;
fo(i,1,n){
changemi(1,1,2*n,b[i],a[i]);
changemx(1,1,2*n,a[i],b[i]);
}
fo(i,1,n)
if (!bz[i]){
head=0;tail=1;
dl[1]=i;
while (head<tail){
now=dl[++head];
bz[now]=1;
while (1){
t=querymi(1,1,2*n,a[now]+1,b[now]-1);
if (t>=a[now]) break;
t=q[t];
merge(t,now);
dl[++tail]=t;
changemi(1,1,2*n,b[t],2*n+1);
changemx(1,1,2*n,a[t],0);
}
while (1){
t=querymx(1,1,2*n,a[now]+1,b[now]-1);
if (t<=b[now]) break;
t=p[t];
merge(t,now);
dl[++tail]=t;
changemi(1,1,2*n,b[t],2*n+1);
changemx(1,1,2*n,a[t],0);
}
}
ans=ans*2%mo;
}
fo(i,1,8*n) mi[i]=2*n+1;
fo(i,1,n){
getfa(i);
if (co[i]==0) changemi(1,1,2*n,b[i],a[i]);
}
fo(i,1,n)
if (co[i]==0&&querymi(1,1,2*n,a[i]+1,b[i]-1)<a[i]){
ans=0;
break;
}
fo(i,1,8*n) mi[i]=2*n+1;
fo(i,1,n){
getfa(i);
if (co[i]==1) changemi(1,1,2*n,b[i],a[i]);
}
fo(i,1,n)
if (co[i]==1&&querymi(1,1,2*n,a[i]+1,b[i]-1)<a[i]){
ans=0;
break;
}
/*fo(i,1,n)
if (!fa[i]) ans=ans*2%mo;*/
printf("%d\n",ans);
}