[二分图] 【JOI2017春季合宿】Port Facility

T2比T1可做多了…

每个玩具相当于一条线段,交叉的线段之间连边,就变成求二分图染色的方案数

考虑怎么建边

把左端点排序,右端点用set维护,假设现在加入的线段为(l,r)

因为有三元环就直接无解了,所以当前所有右端点比r小的线段都是相互包含的,否则就无解

而这时候这些线段之后染色的颜色都是一样的,所以可以缩起来

我的做法是把右端点最小的加入set,当它被弹出set的时候把右端点第二小的加入set

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>
#include <cstdlib>
#define fi first
#define se second

using namespace std;

typedef pair<int,int> pii;

const int N=1000010,P=1e9+7;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

int n,cnt,a[N],b[N],id[N],G[N],t;
set<pii> S;

pii q[N];

struct edge{
  int t,nx;
}E[N<<2];

inline bool cmp(const int &x,const int &y){
  return a[x]<a[y];
}

inline void addedge(int x,int y){
  E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
  E[++cnt].t=x; E[cnt].nx=G[y]; G[y]=cnt;
}

int cl[N];

void dfs(int x,int c){
  if(cl[x]){
    if(cl[x]!=c) puts("0"),exit(0);
    return ;
  }
  cl[x]=c;
  for(int i=G[x];i;i=E[i].nx) dfs(E[i].t,c^1);
}

int nxt[N];

int main(){
  freopen("port_facility.in","r",stdin);
  freopen("port_facility.out","w",stdout);
  read(n);
  for(int i=1;i<=n;i++)
    read(a[i]),read(b[i]),id[i]=i;
  sort(id+1,id+1+n,cmp);
  for(int i=1;i<=n;i++){
    while(!S.empty() && S.begin()->fi<a[id[i]]){
      int cur=S.begin()->se;
      S.erase(S.begin());
      if(nxt[cur]) S.insert(pii(b[nxt[cur]],nxt[cur]));
    }
    set<pii>::iterator cur=S.insert(pii(b[id[i]],id[i])).fi;
    t=0;
    while(cur!=S.begin()){
      cur--; q[++t]=*cur;
    }
    int mx=1<<29;
    for(int j=1;j<=t;j++)
      if(a[q[j].se]>mx) return puts("0"),0;
      else{
    mx=min(mx,q[j].fi);
    addedge(q[j].se,id[i]);
      }
    for(int i=1;i<t;i++) S.erase(q[i]);
    for(int i=2;i<=t;i++) nxt[q[i].se]=q[i-1].se;
  }
  int ans=1;
  for(int i=1;i<=n;i++)
    if(!cl[i]){
      dfs(i,2); ans=2*ans%P; 
    }
  printf("%d\n",ans);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值