BZOJ4025: 二分图

BZOJ4025: 二分图

Description

神犇有一个n个节点的图。因为神犇是神犇,所以在T时间内一些边会出现后消失。神犇要求出每一时间段内这个图是否是二分图。这么简单的问题神犇当然会做了,于是他想考考你。

Input

输入数据的第一行是三个整数n,m,T。
第2行到第m+1行,每行4个整数u,v,start,end。第i+1行的四个整数表示第i条边连接u,v两个点,这条边在start时刻出现,在第end时刻消失。

Output

输出包含T行。在第i行中,如果第i时间段内这个图是二分图,那么输出“Yes”,否则输出“No”,不含引号。

Sample Input

3 3 3
1 2 0 2
2 3 0 3
1 3 1 2

Sample Output

Yes
No
Yes

HINT

样例说明:
0时刻,出现两条边1-2和2-3。
第1时间段内,这个图是二分图,输出Yes。
1时刻,出现一条边1-3。
第2时间段内,这个图不是二分图,输出No。
2时刻,1-2和1-3两条边消失。
第3时间段内,只有一条边2-3,这个图是二分图,输出Yes。
数据范围:
n<=100000,m<=200000,T<=100000,1<=u,v<=n,0<=start<=end<=T。

题解Here!

又是一道神题,好难。。。

开始的时候并不知道$LCT$怎么判二分图。。。

然后翻题解。。。百度一下二分图。

二分图就是在任意时刻,这个图中一定没有奇环。

奇环是什么?就是有奇数个点的环啊。。。

其实就是判每一个时刻有没有奇环。

然后我们考虑如何维护。

我们先随便弄出一棵生成树,然后我们想维护这样的集合$S$:$S$中的任意一条边都会和生成树构成奇环。

有一种情况我们没有办法处理,就是:把集合的边加上,然后,树边没了。。。

我们还要把集合中的边换成树边,非常麻烦,码量贼大。。。

所以我们维护以删除时间为边权的最大生成树,就可以解决以上这种情况了。

然后就是$LCT$板子了,开始码码码。。。

然后我一下午就这么搭进去了。。。

据说还有分治+并查集的算法?反正本蒟蒻不会。。。

orz POPOQQQ姐。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 400010
#define MAX 2147483646
using namespace std;
int n,m,q,ans=0;
int num[MAXN],val[MAXN];
struct Edge{
	int u,v,start,end;
	friend bool operator <(const Edge &p,const Edge &q){
		if(p.start==q.start)return p.end<q.end;
		return p.start<q.start;
	}
}edge[MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
namespace LCT{
	int top=0,stack[MAXN];
	struct Link_Cut_Tree{
		int f,flag,son[2];
		int v,s;
	}a[MAXN];
	inline bool isroot(int rt){
		return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt;
	}
	inline void pushup(int rt){
		if(!rt)return;
		a[rt].s=a[a[rt].son[0]].s+a[a[rt].son[1]].s+1;
		a[rt].v=rt;
		if(val[a[a[rt].son[0]].v]<val[a[rt].v])a[rt].v=a[a[rt].son[0]].v;
		if(val[a[a[rt].son[1]].v]<val[a[rt].v])a[rt].v=a[a[rt].son[1]].v;
	}
	inline void pushdown(int rt){
		if(!rt||!a[rt].flag)return;
		a[a[rt].son[0]].flag^=1;a[a[rt].son[1]].flag^=1;a[rt].flag^=1;
		swap(a[rt].son[0],a[rt].son[1]);
	}
	inline void turn(int rt){
		int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0;
		if(!isroot(x)){
			if(a[y].son[0]==x)a[y].son[0]=rt;
			else a[y].son[1]=rt;
		}
		a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x;
		a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x;
		pushup(x);pushup(rt);
	}
	void splay(int rt){
		top=0;
		stack[++top]=rt;
		for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f;
		while(top)pushdown(stack[top--]);
		while(!isroot(rt)){
			int x=a[rt].f,y=a[x].f;
			if(!isroot(x)){
				if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt);
				else turn(x);
			}
			turn(rt);
		}
	}
	void access(int rt){
		for(int i=0;rt;i=rt,rt=a[rt].f){
			splay(rt);
			a[rt].son[1]=i;
			pushup(rt);
		}
	}
	inline void makeroot(int rt){access(rt);splay(rt);a[rt].flag^=1;}
	int findroot(int rt){
		access(rt);splay(rt);
		while(a[rt].son[0])rt=a[rt].son[0];
		return rt;
	}
	inline void split(int x,int y){makeroot(x);access(y);splay(y);}
	inline void link(int x,int y){makeroot(x);a[x].f=y;}
	inline void cut(int x,int y){split(x,y);a[x].f=a[y].son[0]=0;}
	void add_edge(int now,int now_time){
		int u=edge[now].u,v=edge[now].v;
		if(u==v&&edge[now].end>now_time){
			ans++;
			num[edge[now].end]++;
			return;
		}
		if(findroot(u)!=findroot(v)){link(now+n,u);link(v,now+n);}
		else{
			int x;
			bool flag=false;
			split(u,v);
			if(!((a[v].s>>1)&1))flag=true;
			if(val[a[v].v]>=edge[now].end)x=now;
			else{
				x=a[v].v-n;
				cut(x+n,edge[x].u);cut(x+n,edge[x].v);
				link(now+n,u);link(v,now+n);
			}
			if(flag&&edge[x].end>now_time){
				ans++;
				num[edge[x].end]++;
			}
		}
	}
}
void work(){
	int now=1;
	for(int i=1;i<=q;i++){
		while(now<=m&&edge[now].start<=i){
			LCT::add_edge(now,i);
			now++;
		}
		ans-=num[i];
		if(ans)printf("No\n");
		else printf("Yes\n");
	}
}
void init(){
	n=read();m=read();q=read();
	val[0]=MAX;
	for(int i=1;i<=n;i++){
		val[i]=MAX;
		LCT::a[i].s=1;
		LCT::a[i].v=i;
	}
	for(int i=1;i<=m;i++){
		edge[i].u=read();edge[i].v=read();
		edge[i].start=read()+1;edge[i].end=read()+1;
	}
	sort(edge+1,edge+m+1);
	for(int i=1;i<=m;i++){
		val[i+n]=edge[i].end;
		LCT::a[i+n].s=1;
		LCT::a[i+n].v=i+n;
	}
}
int main(){
	init();
	work();
    return 0;
}

 

posted @ 2018-08-29 17:11  符拉迪沃斯托克  阅读(279)  评论(0编辑  收藏  举报
Live2D