Problem
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;
}