BZOJ4025 二分图题解(LCT)

题目:BZOJ4025.
题目大意:给定一张 n n n个点 m m m条边的无向图,以及每条边的出现时间段 [ l i , r i ] [l_i,r_i] [li,ri],问每个时间点的图是否是二分图.
1 ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 2 ∗ 1 0 5 1\leq n\leq 10^5,1\leq m\leq 2*10^5 1n105,1m2105,结束时间 ≤ 1 0 5 \leq 10^5 105.

线段树分治+并查集做法.

考虑二分图的性质,想到一张图为二分图的条件为没有奇环,往这个方向往下想.

考虑问题其实就是给一张图加边或删边,查询图是否是二分图这个操作是一个标准的动态图模型,而动态图一般是用动态树维护图的一个生成树来实现的,所以考虑使用LCT来维护图的一棵生成树.

于是考虑加入一条边的两种情况:
1.加入一条边使得两个联通块合并.这个时候可以直接操作.
2.加入的边的两个端点在同一个联通块中.这个时候我们需要判定是否是奇环,若是奇环则给奇环数量 + 1 +1 +1.

考虑到删边的问题,我们再给每条边打上一个边权为它的删除时间.这样子的话我们尽量维护一棵原图的最大生成树,也就是让所有边的边权都尽量大.这样子的话我们要删LCT上的边的时候,肯定没有其他可以代替这条边的边可以删了.同时我们把奇环标记总打在这个奇环上最早消失的边(即奇环中不在树上的边)上,这样就可以快速维护奇环了.

具体实现中当然不可能直接维护边了,所以拆个边就好,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000,M=200000,INF=(1<<30)-1;

vector<int> s[N+9],t[N+9];
int u[M+9],v[M+9],b[M+9],e[M+9],flag[M+9],cnt;
struct tree{
  int ss,x,min,fa,s[2],rev,siz;
}tr[N+M+9];
int tt,tmp[N+M+9],n,m,T;

bool Isroot(int x){return tr[tr[x].fa].s[0]^x&&tr[tr[x].fa].s[1]^x;}
int Min(int x,int y){return e[x]<e[y]?x:y;}

void Pushup(int x){
  int ls=tr[x].s[0],rs=tr[x].s[1];
  tr[x].siz=tr[ls].siz+tr[x].ss+tr[rs].siz;
  tr[x].min=Min(Min(tr[ls].min,tr[rs].min),tr[x].x);
}

void Update_rev(int x){tr[x].rev^=1;swap(tr[x].s[0],tr[x].s[1]);}

void Pushdown(int x){
  if (!tr[x].rev) return;
  tr[x].rev=0;
  Update_rev(tr[x].s[0]);Update_rev(tr[x].s[1]); 
}

void Rotate(int x){
  int y=tr[x].fa,z=tr[y].fa,k=tr[y].s[1]==x;
  if (!Isroot(y)) tr[z].s[tr[z].s[1]==y]=x;tr[x].fa=z;
  tr[y].s[k]=tr[x].s[k^1];if (tr[x].s[k^1]) tr[tr[x].s[k^1]].fa=y;
  tr[y].fa=x;tr[x].s[k^1]=y;
  Pushup(y);Pushup(x);
}

void Splay(int x){
  tmp[tt=1]=x;
  for (int i=x;!Isroot(i);i=tr[i].fa) tmp[++tt]=tr[i].fa;
  for (;tt;--tt) Pushdown(tmp[tt]);
  int y,z;
  while (!Isroot(x)){
  	y=tr[x].fa,z=tr[y].fa;
  	if (!Isroot(y)) tr[z].s[1]==y^tr[y].s[1]==x?Rotate(x):Rotate(y);
  	Rotate(x);
  }
}

void Access(int x){for (int t=0;x;t=x,x=tr[x].fa) Splay(x),tr[x].s[1]=t,Pushup(x);}
void Makeroot(int x){Access(x);Splay(x);Update_rev(x);}
int Root(int x){Access(x);Splay(x);while (tr[x].s[0]) x=tr[x].s[0];return x;}
void Split(int x,int y){Makeroot(x);Access(y);Splay(y);}
void Link(int x,int y){if (Root(x)==Root(y)) return;Makeroot(x);tr[x].fa=y;}
void Cut(int x,int y){Split(x,y);if (tr[y].s[0]^x||tr[y].s[1]||tr[x].s[1]) return;tr[y].s[0]=tr[x].fa=0;}
int Query_min(int x,int y){Split(x,y);return tr[y].min;}
int Query_siz(int x,int y){Split(x,y);return tr[y].siz;}
void Change(int x,int y){Access(x);Splay(x);tr[x].x=y;Pushup(x);}
void Change_siz(int x,int y){Access(x);Splay(x);tr[x].ss=y;Pushup(x);} 

void Add_side(int id){
  if (Root(u[id])^Root(v[id])){
  	Link(u[id],id+n);Link(v[id],id+n);
	flag[id]=1;
  }else{
  	int tmp=Query_min(u[id],v[id]);
  	if (e[tmp]<e[id]){
  	  if (flag[tmp]==2) --cnt;
  	  Cut(u[tmp],tmp+n);Cut(v[tmp],tmp+n);
  	  Link(u[id],id+n);Link(v[id],id+n);
  	  flag[id]=1;id=tmp;
  	}
  	if (Query_siz(u[id],v[id])&1) flag[id]=2,++cnt;
  	else flag[id]=3;
  }
}

void Era_side(int id){
  if (flag[id]==2) --cnt;
  if (flag[id]==1) Cut(u[id],id+n),Cut(v[id],id+n); 
  flag[id]=0;
}

Abigail into(){
  scanf("%d%d%d",&n,&m,&T);
  for (int i=1;i<=m;++i){
  	scanf("%d%d%d%d",&u[i],&v[i],&b[i],&e[i]);
  	s[b[i]].push_back(i);
  	t[e[i]].push_back(i);
  	Change(i+n,i);
  }
}

Abigail work(){
  e[0]=INF;
  for (int i=1;i<=n;++i) Change_siz(i,1);
  for (int i=1;i<=T;++i){
  	for (int j=0;j<s[i-1].size();++j)
  	  Add_side(s[i-1][j]);
    puts(cnt==0?"Yes":"No");
  	for (int j=0;j<t[i].size();++j)
  	  Era_side(t[i][j]);
  }
}

int main(){
  into();
  work();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值