原题链接: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;
}