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。
解题思路:
CDQ分治太高端,还是LCT好想一点。
一个图是二分图的充要条件:没有奇环。
关于环的问题一般要转化为生成树的问题,一条非树边就是一个简单环,即考虑非树边的影响。
而一个奇环的影响仅取决于环上删除时间最小的边,所以我们相当于要动态维护当前时刻关于删除时间的最大生成树,上LCT。
对于每个时刻,加入一条边时,若它为树边直接加,若形成环,则弹出环上删除时间最小的边,若同时为奇环,则将最小边加入一个集合,表示这条边存在时,图中总有奇环(偶环不用加这条边,因为若之后加入的边与这条边形成了奇环,那么新边一定能与其他边构成奇环)。
删去一条边时,若为树边直接删,就若在集合中,在集合中删去。
这样,在每一时刻,若集合大小为0则图为二分图。
注意LCT维护生成树不是把边权附到点上,而是把边也看做点向两个端点连边。那么边的sz为0,权值为删除时间;点sz为1,权值为正无穷。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=500005,INF=10000000;
struct edge{int x,y,st,ed;}bian[N];
struct node{int t,id;}e1[N],e2[N];
inline bool cmp(const node &a,const node &b){return a.t<b.t;}
int n,m,T,cnt,top;
int fa[N],son[N][2],id[N],mn[N],val[N],size[N],rev[N],stk[N];
bool ontree[N],in[N];
int which(int x){return son[fa[x]][1]==x;}
bool rt(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
int Min(int x,int y){return bian[x].ed<bian[y].ed?x:y;}
void update(int x)
{
size[x]=size[son[x][0]]+size[son[x][1]]+val[x];
mn[x]=Min(id[x],Min(mn[son[x][0]],mn[son[x][1]]));
}
void pushdown(int x)
{
swap(son[x][0],son[x][1]);
rev[x]=0;
if(son[x][0])rev[son[x][0]]^=1;
if(son[x][1])rev[son[x][1]]^=1;
}
int rotate(int x)
{
int y=fa[x],z=fa[y],t=which(x);
if(!rt(y))son[z][which(y)]=x;
fa[x]=z,fa[y]=x;
son[y][t]=son[x][t^1],son[x][t^1]=y;
if(son[y][t])fa[son[y][t]]=y;
update(y),update(x);
}
void splay(int x)
{
stk[top=1]=x;
for(int i=x;!rt(i);i=fa[i])stk[++top]=fa[i];
for(int i=top;i;i--)if(rev[stk[i]])pushdown(stk[i]);
while(!rt(x))
{
if(!rt(fa[x]))
which(x)==which(fa[x])?rotate(fa[x]):rotate(x);
rotate(x);
}
}
void access(int x)
{
for(int y=0;x;y=x,x=fa[x])
{
splay(x),son[x][1]=y,update(x);
if(y)fa[y]=x;
}
}
int findroot(int x)
{
access(x),splay(x);
while(son[x][0])x=son[x][0];
return x;
}
void makeroot(int x)
{
access(x),splay(x),rev[x]^=1;
}
void link(int x,int y)
{
makeroot(x),fa[x]=y;
}
void cut(int x,int y)
{
makeroot(x),access(y),splay(y);
fa[x]=son[y][0]=0;
}
int main()
{
//freopen("lx.in","r",stdin);
//freopen("lx.out","w",stdout);
n=getint(),m=getint(),T=getint();
bian[0].ed=INF;
for(int i=1;i<=n;i++)val[i]=1;
for(int i=1;i<=m;i++)
{
bian[i].x=getint(),bian[i].y=getint();
bian[i].st=e1[i].t=getint(),bian[i].ed=e2[i].t=getint();
e1[i].id=e2[i].id=id[i+n]=mn[i+n]=i;
}
sort(e1+1,e1+m+1,cmp),sort(e2+1,e2+m+1,cmp);
for(int t=0,i=1,j=1;t<T;t++)
{
while(e1[i].t<=t&&i<=m)
{
int e=e1[i].id,x=bian[e].x,y=bian[e].y;
if(findroot(x)!=findroot(y))link(x,e+n),link(e+n,y),ontree[e]=1;
else
{
makeroot(x),access(y),splay(y);
int tmp=mn[y],sz=size[y];
if(bian[tmp].ed<bian[e].ed)
{
cut(bian[tmp].x,tmp+n),cut(tmp+n,bian[tmp].y),ontree[tmp]=0;
link(x,e+n),link(e+n,y),ontree[e]=1;
e=tmp;
}
if(sz&1)cnt++,in[e]=1;
}
i++;
}
while(e2[j].t<=t&&j<=m)
{
int e=e2[j].id,x=bian[e].x,y=bian[e].y;
if(ontree[e])cut(x,e+n),cut(e+n,y),ontree[e]=0;
if(in[e])cnt--,in[e]=0;
j++;
}
cnt?puts("No"):puts("Yes");
}
return 0;
}