题目描述
传送门
题目大意:有一个n个节点的图。在T时间内一些边会出现后消失。出每一时间段内这个图是否是二分图。
题解
如何判断一个图是不是二分图?对图进行黑白染色,如果一个点被染成了两种颜色,那么就不是,否则是二分图。
那如果不染色能不能判断?判断一下图中是否有奇环,如果有奇环的话一定不是二分图。注意自环也算做奇环。
那么我们以消失时间end为边权,维护最大生成树。对于加入的边分成三类,在树中的边,奇环的非树边,偶环的非树边。我们只考虑所有只有一条非树边的环,剩下的环其实都是环套环。奇环的非树边加入会对奇环的个数+1,同时奇环的非树边删除会对奇环的个数-1,(因为环的非树边其实就是end最小的,拆掉他环就展成了链)。
还需要用优先队列维护加入的边,使他们按照end从小到大,便于依次的删除。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#define N 400003
#define inf 1000000000
using namespace std;
struct data{
int x,y,s,t,id,pd;
bool operator <(const data &a) const{
return t>a.t;
}
}tr[N];
priority_queue<data> p;
int n,m,T,fa[N],ch[N][2],size[N],key[N],pos[N],rev[N],top,st[N],mark[N];
int cmp(data a,data b){
return a.s<b.s||a.s==b.s&&a.t<b.t;
}
bool isroot(int x)
{
return ch[fa[x]][1]!=x&&ch[fa[x]][0]!=x;
}
int get(int x)
{
return ch[fa[x]][1]==x;
}
void pushdown(int now)
{
if (!now) return;
if (rev[now]) {
swap(ch[now][0],ch[now][1]);
rev[ch[now][0]]^=1; rev[ch[now][1]]^=1;
rev[now]=0;
}
}
void update(int now)
{
int l=ch[now][0]; int r=ch[now][1];
pos[now]=now;
if (key[now]!=inf) size[now]=1;
else size[now]=0;
if (l) {
if (key[pos[l]]<key[pos[now]]) pos[now]=pos[l];
size[now]+=size[l];
}
if (r) {
if (key[pos[r]]<key[pos[now]]) pos[now]=pos[r];
size[now]+=size[r];
}
}
void rotate(int x)
{
int y=fa[x]; int z=fa[y]; int which=get(x);
if (!isroot(y)) ch[z][ch[z][1]==y]=x;
ch[y][which]=ch[x][which^1]; fa[ch[y][which]]=y;
ch[x][which^1]=y; fa[y]=x; fa[x]=z;
update(y); update(x);
}
void splay(int x)
{
top=0; st[++top]=x;
for (int i=x;!isroot(i);i=fa[i]) st[++top]=fa[i];
for (int i=top;i>=1;i--) pushdown(st[i]);
while (!isroot(x)) {
int y=fa[x];
if (!isroot(y)) rotate(get(y)==get(x)?y:x);
rotate(x);
}
}
void access(int x)
{
int t=0;
while (x) {
splay(x);
ch[x][1]=t; update(x);
t=x; x=fa[x];
}
}
void rever(int x)
{
access(x); splay(x); rev[x]^=1;
}
void link(int x,int y)
{
rever(x); fa[x]=y; splay(x); update(x);
}
void cut(int x,int y)
{
rever(x); access(y); splay(y);
fa[x]=ch[y][0]=0; update(y);
}
int find(int x)
{
access(x); splay(x);
while(ch[x][0]) x=ch[x][0];
return x;
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d%d%d",&n,&m,&T);
for (int i=1;i<=m;i++) scanf("%d%d%d%d",&tr[i].x,&tr[i].y,&tr[i].s,&tr[i].t);
sort(tr+1,tr+m+1,cmp);
//for (int i=1;i<=m;i++) cout<<tr[i].x<<" "<<tr[i].y<<" "<<tr[i].s<<" "<<tr[i].t<<endl;
for (int i=1;i<=m;i++) tr[i].id=i;
for (int i=1;i<=n;i++) pos[i]=i,key[i]=inf;
int cnt=0; int k=0; int l=0;
for (int i=0;i<=T;i++) {
//for (int j=1;j<=n+m;j++) cout<<fa[j]<<" ";
//cout<<endl;
if (i!=0) {
if (cnt||k) printf("No\n");
else printf("Yes\n");
}
while (l<m&&tr[l+1].s<=i) {
l++; p.push(tr[l]);
if (tr[l].x==tr[l].y) {
k++; continue;
}
if (find(tr[l].x)!=find(tr[l].y)) {
pos[l+n]=l+n; key[l+n]=tr[l].t; size[l+n]=1;
link(tr[l].x,l+n);
link(tr[l].y,l+n);
mark[tr[l].id]=1;
}
else {
rever(tr[l].x);
access(tr[l].y);
splay(tr[l].y);
int t=pos[tr[l].y]; int c=0;
if (!(size[tr[l].y]&1)) cnt++,c++;
if (key[t]<tr[l].t) {
mark[t-n]=2+c; mark[tr[l].id]=1;
cut(t,tr[t-n].x); cut(t,tr[t-n].y);
pos[l+n]=l+n; key[l+n]=tr[l].t; size[l+n]=1;
link(tr[l].x,l+n); link(tr[l].y,l+n);
}
else mark[tr[l].id]=2+c;
}
}
while (!p.empty()) {
data a=p.top();
if (a.t==i) {
if (a.x==a.y) {
k--; p.pop(); continue;
}
if (mark[a.id]==1) cut(a.id+n,a.x),cut(a.id+n,a.y),mark[a.id]=0;
else{
rever(a.x); access(a.y); splay(a.y);
if (mark[a.id]==3) cnt--;
mark[a.id]=0;
}
p.pop();
}else break;
}
}
}