【LOJ #2391】「JOISC 2017 Day 1」港口设施(二分图染色 / 并查集)

传送门

显然可以看成把有交叉的船之间连边跑二分图染色
但是边数是 O ( n 2 ) O(n^2) O(n2)的,考虑优化

考虑按时间轴从小到大枚举
对于当前时间如果是船 i i i B i B_i Bi
即向左端点在 ( A i , B i ) (A_i,B_i) (Ai,Bi)且还没枚举过右端点的船连边
这样复杂度是 O ( ∣ E ∣ ) O(|E|) O(E)

考虑进一步优化
可以发现如果存在两个点分别向
[ a , b ] , [ c , d ] [a,b],[c,d] [a,b],[c,d]连边
a < c < b < d a<c<b<d a<c<b<d
那么实际上由于 [ a , b ] , [ c , d ] [a,b],[c,d] [a,b],[c,d]内都是同色的
先练了 [ a , b ] [a,b] [a,b]第二次就不需要向 [ c , b ) [c,b) [c,b)连边了
这样的话边数是 O ( n ) O(n) O(n)

可以对每个维护一个 n x t nxt nxt表示向右最大的同色的位置
从左往右用并查集维护

具体可以看代码
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
#define cs const
#define re register
#define pb push_back
#define pii pair<int,int>
#define ll long long
#define fi first
#define se second
#define bg begin
cs int RLEN=1<<20|1;
inline char gc(){
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob)?EOF:*ib++;
}
inline int read(){
    char ch=gc();
    int res=0;bool f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
template<class tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<class tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int mod=1e9+7;
inline int add(int a,int b){return (a+=b)>=mod?(a-mod):a;}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){static ll r;r=1ll*a*b;return (r>=mod)?(r%mod):r;}
inline void Add(int &a,int b){(a+=b)>=mod?(a-=mod):0;}
inline void Dec(int &a,int b){a-=b,a+=a>>31&mod;}
inline void Mul(int &a,int b){static ll r;r=1ll*a*b;a=(r>=mod)?(r%mod):r;}
inline int ksm(int a,int b,int res=1){if(a==0&&b==0)return 0;for(;b;b>>=1,Mul(a,a))(b&1)&&(Mul(res,a),1);return res;}
inline int Inv(int x){return ksm(x,mod-2);}
inline int fix(int x){return (x<0)?x+mod:x;}
cs int N=1000005;
vector<int> e[N];
int col[N];
int nxt[N],fa[N],le[N],id[N<<1],a[N],cnt;
int n;
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void addedge(int u,int v){
	e[u].pb(v),e[v].pb(u);
}
int dfs(int u){
	for(int &v:e[u]){
		if(col[v]!=-1&&col[v]==col[u])return 0;
		if(col[v]==-1){
			col[v]=col[u]^1;
			if(!dfs(v))return 0;
		}
	}
	return 1;
}
int main(){
	#ifdef Stargazer
	freopen("lx.in","r",stdin);
	#endif
	n=read();
	for(int i=1;i<=n;i++){
		int l=read(),r=read();
		id[l]=id[r]=i;
	}
	for(int i=1;i<=n;i++)nxt[i]=i,fa[i]=i;
	fa[n+1]=n+1;
	for(int i=1;i<=2*n;i++){
		int p=id[i];
		if(!le[p])a[++cnt]=p,le[p]=cnt;
		else{
			int u=le[p];
			fa[u]=find(u+1);
			for(int j=fa[u],k;j<=cnt;j=k)
			addedge(a[j],p),k=find(nxt[j]+1),nxt[j]=cnt;
		}
	}
	int cnt=0;
	memset(col,-1,sizeof(col));
	for(int i=1;i<=n;i++)if(col[i]==-1){col[i]=0;
		if(dfs(i))cnt++;
		else {puts("0");return 0;}
	}
	cout<<ksm(2,cnt)<<'\n';
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值