原题链接: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,把出现一条边看做 l i n k link link,消失看做 c u t cut cut,按时间将操作离线,如果出现了奇环,这就是个二分图。
但是这是个图, L C T \mathcal{LCT} LCT总有 l i n k link link出环的时候,所以我们只在 L C T \mathcal{LCT} LCT中保存一个以删除时间为关键值的最大生成树, l i n k link link时如果出现了环,将删除时间最早的边从 L C T \mathcal{LCT} LCT中删去,如果形成的是奇环,还要进行标记,奇环数 + 1 +1 +1;
删除时,如果该边本来就在 L C T \mathcal{LCT} LCT里,直接删去。如果不在 L C T \mathcal{LCT} LCT里且为我们标记过的奇环边,去掉标记,奇环数 − 1 -1 −1。
在本时间点的操作全部做完以后,根据奇环数回答询问即可。
代码
#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);
}
void splay(int v){push(v);for(int f,ff;notroot(v);spin(v)){f=dad[v],ff=dad[f];if(notroot(f))spin((son[f][0]==v)^(son[ff][0]==f)?v:f);}up(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,i;
for(scanf("%d%d%d",&n,&m,&t),memset(val,127,sizeof(val)),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;}
else
{
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()
{
sort(ope+1,ope+1+top);
for(int i=0,j=1,x,y,id;i<t;ans?puts("No"):puts("Yes"))
for(++i;ope[j].t<i&&j<=top;++j){x=ope[j].a1,y=ope[j].a2,id=ope[j].id;ope[j].op?deal2(x,y,id):deal1(x,y,id,edge[id].t);}
}
int main(){in();ac();}