港湾

题目描述

放假啦!
小林和康娜来到了港口,看到有货船正在卸货。
港口十分狭窄,只有两个卸货区可以使用。每个卸货区上面可以堆积任意多个箱子。
每卸下来一个箱子,工作人员都会把这个箱子放在某个卸货区的顶端。之后,当车辆来运走这个箱子的时候,也必须保证这个箱子在某个卸货区的顶端。
港口今天一共运来了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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值