BZOJ4025 二分图

原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4025

二分图

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。

题解

首先,根据奇妙的性质,当一个图里面存在边数为奇数的环时,该图不是二分图。

很直接的做法就出来了,我们维护一下以消失时间为边权的 L C T \mathcal{LCT} LCT,当加边的时候,分情况讨论:

1.不成环,那么愉快的接上去。

2.成环了,考虑怎么维护:首先,如果图中已经出现了奇环,无论我们再连什么边,奇环始终存在,也不会对后面环的存在性有影响(可以画几个图试试),那么我们就不需要再维护那个奇环了。我们先找到连点路径上删除时间最早的边,跟当前要加入的边比较一下删除的时间,删掉删除时间较早的即可,再把另一条边加进去。删掉的那条边我们另开一个数组记录,这样,当前时间点存在的边就被分为三类:(1)存在但不在 L C T \mathcal{LCT} LCT上的,能使图上存在奇环的边;(2)存在但不在 L C T \mathcal{LCT} LCT上的,不能使图上存在奇环的边;(3)存在且在 L C T \mathcal{LCT} LCT上的。对于(1)(3)类边,我们开数组记录下来,(2)类直接扔掉即可。

删边的时候就很简单了:

1.如果该边为(1)类,把(1)类标记去掉就好了,奇环数-1。

2.如果为(2)类,到 L C T \mathcal{LCT} LCT上把它剪掉,去掉(2)类标记。

3.其他的不管。

代码
#include<bits/stdc++.h>
#define ls son[v][0]
#define rs son[v][1]
using namespace std;
const int M=1e6+5;
struct sd{int op,a1,a2,t,id;};
struct ed{int a1,a2,t;};
bool operator <(sd a,sd b){return a.t<b.t;}
int dad[M],son[M][2],siz[M],mn[M],val[M],n,m,t,top,ans;
bool rev[M],gg[M],on[M];sd ope[M];ed edge[M];
bool notroot(int v){return son[dad[v]][1]==v||son[dad[v]][0]==v;}
void up(int v)
{
	siz[v]=(v>n)+siz[ls]+siz[rs];mn[v]=v;
	if(val[mn[v]]>val[mn[ls]])mn[v]=mn[ls];  
	if(val[mn[v]]>val[mn[rs]])mn[v]=mn[rs];
}
void turn(int v){swap(ls,rs);rev[v]^=1;}
void down(int v){if(!rev[v])return;if(ls)turn(ls);if(rs)turn(rs);rev[v]=0;}
void push(int v){if(notroot(v))push(dad[v]);down(v);}
void spin(int v)
{
	int f=dad[v],ff=dad[f],k=son[f][1]==v,w=son[v][!k];
	if(notroot(f))son[ff][son[ff][1]==f]=v;
	son[v][!k]=f;son[f][k]=w;
	if(w)dad[w]=f;
	dad[f]=v;dad[v]=ff;
	up(f);up(v);
}
void splay(int v)
{
	push(v);
	int f,ff;
	while(notroot(v))
	{
		f=dad[v];ff=dad[f];
		if(notroot(f))spin((son[f][0]==v)^(son[ff][0]==f)?v:f);
		spin(v);
	}
}
void access(int v){for(int f=0;v;v=dad[f=v])splay(v),rs=f,up(v);}
void beroot(int v){access(v);splay(v);turn(v);}
void split(int x,int y){beroot(x);access(y);splay(y);}
void link(int x,int y){beroot(x);dad[x]=y;}
void cut(int x,int y){split(x,y);dad[x]=son[y][0]=0;up(y);}
int findroot(int v){access(v);splay(v);while(ls)push(v),v=ls;return v;}
void in()
{
	int a,b,c,d;
	scanf("%d%d%d",&n,&m,&t);
	memset(val,127,sizeof(val));
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d%d%d",&a,&b,&c,&d);
		val[n+i]=d;
		ope[++top]=(sd){0,a,b,c,i};
		ope[++top]=(sd){1,a,b,d,i};
		edge[i]=(ed){a,b,d};
	}
}
void deal1(int x,int y,int id,int lim)
{
	int hh;
	if(x==y){ans++;gg[id]=1;return;}
	beroot(x);
	if(findroot(y)!=x){link(y,id+n);link(x,id+n);on[id]=1;return;}
	split(x,y);
	if(val[mn[y]]<lim)
	{
		hh=mn[y]-n;
		if(siz[y]&1^1)gg[hh]=1,ans++;
		cut(edge[hh].a1,hh+n);cut(edge[hh].a2,hh+n);link(x,id+n);link(y,id+n);
		on[hh]=0;on[id]=1;
	}
	else if(siz[y]&1^1)gg[id]=1,ans++;
}
void deal2(int x,int y,int id)
{
	if(on[id])cut(x,id+n),cut(y,id+n),on[id]=0;
	else if(gg[id])gg[id]=0,ans--;
}
void ac()
{
	int x,y,id;
	sort(ope+1,ope+1+top);
	for(int i=0,j=1;i<t;)
	{
		++i;
		for(;ope[j].t<i&&j<=top;++j)
		{
			x=ope[j].a1,y=ope[j].a2,id=ope[j].id;
			if(!ope[j].op)deal1(x,y,id,edge[id].t);
			else deal2(x,y,id);
		}
		if(!ans)puts("Yes");
		else puts("No");
	}
}
int main()
{
	in();ac();
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值