BZOJ4025 二分图

19 篇文章 0 订阅
5 篇文章 0 订阅

Problem

BZOJ

Solution

关于二分图的判定,我们可以考虑原图的一个生成树,然后对于所有非树边,我们统计一下奇环个数即可

然后可以用时间线段树,带权并查集维护一下路径上的点的个数,时间复杂度是 O ( m log ⁡ T log ⁡ n ) O(m\log T\log n) O(mlogTlogn)

也可以用lct维护时间最大生成树,注意一下细节即可,时间复杂度是 O ( m log ⁡ n ) O(m\log n) O(mlogn)。(提供一个调试的好方法,在erase那里把那句注释掉的assert加上)

数据比较坑,注意可能会自环,还可能存在边start=end。

Code

//#include <assert.h>
#include <algorithm>
#include <cstdio>
#define rg register
#define pd(x) if(rev[x])pushdown(x);
using namespace std;
typedef long long ll;
const int maxn=300010,INF=0x3f3f3f3f;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct STACK{
	int tp,a[maxn];
	inline void clear(){tp=0;}
	inline int top(){return a[tp];}
	inline void push(int x){a[++tp]=x;}
	inline void pop(){tp--;}
};
struct data{
	int u,v,id,op,t;
	bool operator < (const data &b)const{return t<b.t;}
}a[maxn<<1],b[maxn];
struct LCT{
	int val[maxn],ch[maxn][2],f[maxn],rev[maxn],sz[maxn],mn[maxn];
	STACK stk;
	inline int isroot(int x){return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
	void pushup(int x)
	{
		if(val[mn[ch[x][0]]]<val[mn[ch[x][1]]]) mn[x]=mn[ch[x][0]];
		else mn[x]=mn[ch[x][1]];
		if(val[x]<val[mn[x]]) mn[x]=x;
		sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
	}
	void pushdown(int x)
	{
		rev[ch[x][0]]^=1;rev[ch[x][1]]^=1;rev[x]=0;
		swap(ch[x][0],ch[x][1]);
	}
	void rotate(int x)
	{
		int fa=f[x],ff=f[fa],l,r;
		l=(ch[fa][1]==x);r=l^1;
		if(!isroot(fa)){if(ch[ff][0]==fa) ch[ff][0]=x;else ch[ff][1]=x;}
		f[x]=ff;f[fa]=x;f[ch[x][r]]=fa;
		ch[fa][l]=ch[x][r];ch[x][r]=fa;
		pushup(fa);pushup(x);
	}
	void splay(int x)
	{
		stk.clear();stk.push(x);
		for(int i=x;!isroot(i);i=f[i]) stk.push(f[i]);
		for(;stk.tp;stk.pop()) pd(stk.top());
		while(!isroot(x))
		{
			int fa=f[x],ff=f[fa];
			if(!isroot(fa))
			{
				if((ch[fa][0]==x)^(ch[ff][0]==fa)) rotate(x);
				else rotate(fa);
			}
			rotate(x);
		}
	}
	void access(int x){for(int t=0;x;t=x,x=f[x]) splay(x),ch[x][1]=t,pushup(x);}
	void makeroot(int x){access(x);splay(x);rev[x]^=1;}
	int find(int x){access(x);splay(x);while(ch[x][0]) x=ch[x][0];return x;}
	void split(int x,int y)
	{
		makeroot(x);
		access(y);
		splay(y);}
	void cut(int x,int y){split(x,y);if(ch[y][0]==x) ch[y][0]=0,f[x]=0;}
	void link(int x,int y){makeroot(x);f[x]=y;}
}T;
int n,m,t,p=1,cnt,tree[maxn<<1];
void input()
{
	int u,v,s,e;
	read(n);read(m);read(t);
	for(rg int i=0;i<=n;i++) T.val[i]=INF;
	for(rg int i=1;i<=m;i++)
	{
		read(u);read(v);read(s);read(e);
		if(s==e){--i;--m;continue;}
		a[i+i-1]=(data){u,v,i,1,s};a[i<<1]=(data){u,v,i,0,e};
		b[i]=(data){u,v,i,s,e};T.val[n+i]=e;
	}
	m<<=1;sort(a+1,a+m+1);
}
void insert(int id)
{
	if(a[id].u==a[id].v){++cnt;return ;}
	if(T.find(a[id].u)==T.find(a[id].v))
	{
		T.split(a[id].u,a[id].v);
		int num=T.mn[a[id].v]-n,tmp=(T.sz[a[id].v]+1)>>1;
		if(tmp&1) ++cnt;
		if(T.val[num+n]>=b[a[id].id].t) return ;
		T.cut(b[num].u,b[num].id+n);T.cut(b[num].v,b[num].id+n);
		tree[num]=0;
	}
	T.link(a[id].u,a[id].id+n);T.link(a[id].v,a[id].id+n);
	tree[a[id].id]=1;
}
void erase(int id)
{
	if(tree[a[id].id])
	{
		T.cut(a[id].u,a[id].id+n);T.cut(a[id].v,a[id].id+n);
	}
	else
	{
		if(a[id].u==a[id].v){--cnt;return ;}
		//assert(T.find(a[id].u)==T.find(a[id].v));
		T.split(a[id].u,a[id].v);
		int tmp=(T.sz[a[id].v]+1)>>1;
		if(tmp&1) --cnt;
	}
	tree[a[id].id]=0;
}
int main()
{
	input();
	for(rg int i=0;i<t;i++)
	{
		for(;p<=m&&a[p].t<=i;++p)
		{
			if(a[p].op) insert(p);
			else erase(p);
		}
		puts(cnt?"No":"Yes");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值