没有奇环的图就是二分图,所以我们要想办法去维护一个图里面有没有二分图。
线段树分治的做法:以时间建立线段树,对于每一条边的出现与消失时间,我们通过区间更新把这条边挂在线段树的结点上,最后对线段树进行一次遍历。搜索的时候加边,回溯撤销边。用扩展域并查集判断是否为二分图,因为要撤销边,所以并查集得是可撤销的,并且不能路径压缩,同时为了保证复杂度必须按秩合并。
并查集判断二分图的方法是 对于每一条加入的边 (u,v) 如果u,v已经联通 那么不是二分图,否则合并(u,v+n),(v,u+n)
整个复杂度 应该是 mlognlogk
#include<bits/stdc++.h>
#define N 200201
using namespace std;
int n,m,k;
int U[N],V[N];
int fa[N],d[N];
int st1[N],st2[N],top;
vector<int> t[N+N];
void ins(int p,int l,int r,int x,int y,int id)
{
if (l>=x&&r<=y){t[p].push_back(id);return;}
int mid=(l+r)>>1;
if (x<=mid) ins(p<<1,l,mid,x,y,id);
if (y>mid) ins(p<<1|1,mid+1,r,x,y,id);
}
int find(int x)
{
while (x^fa[x]) x=fa[x];
return x;
}
void merge(int x,int y)
{
if (x==y) return;
if (d[x]>d[y]) swap(x,y);
fa[st1[++top]=x]=y,d[y]+=st2[top]=(d[x]==d[y]);
}
void query(int p,int l,int r)
{
int i,j,ok=1,x,u,v,mid=(l+r)>>1,T=top;
for (i=0; i<t[p].size(); i++)
{
x=t[p][i],u=find(U[x]),v=find(V[x]);
if (u==v)
{
for (j=l; j<=r; j++) puts("No");
ok=0;break;
}
merge(find(U[x]+n),v),merge(find(V[x]+n),u);
}
// printf("st1=%d st2=%d\n",st1[top],st2[top]);
// for(int i = 1; i <= 2*n; i++) printf("fa[%d]=%d d[%d]=%d\n",i,fa[i],i,d[i]);
if (ok)
{
if (l==r) puts("Yes");
else
query(p<<1,l,mid),query(p<<1|1,mid+1,r);
}
while (top>T)
d[fa[st1[top]]]-=st2[top],fa[st1[top]]=st1[top],top--;
// for(int i = 1; i <= 2*n; i++) printf("fa[%d]=%d d[%d]=%d\n",i,fa[i],i,d[i]);
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
int i,l,r;
for (i=1; i<=m; i++)
{
scanf("%d%d%d%d",&U[i],&V[i],&l,&r);
if (l^r) ins(1,1,k,l+1,r,i);
}
for (i=1; i<=2*n; i++) fa[i]=i;
query(1,1,k);
return 0;
}
/*
3 1 1
1 2 0 1
*/
另外就是lct的方法了,写起来有点麻烦,不过复杂度很好,只有klogm 我们用lct维护最大消失时间树(瞎起的) 就是每次我们加入一条边 如果它和链(u,v)构成了环 我们就把(u,v)上消失时间最早的边和它 如果它最小 则不操作 否则去掉最小边
我们遍历时间点(不停的加入边和删除边)并且维护一个不可以形成二分图的时间戳,当当前的时间大于等于时间戳时,可以构成二分图,否则不能
另外 构成环的时候 我们观察(u,v)的边是否为偶数,因为加上当前边就变成了奇数环了 如果是构成奇数环,我们更新时间戳为当前奇数环中最先消失的边的时间 注意 这个题的数据有重边和自环(搞了好久re) 注意细节 不然就要见祖宗了
#include<bits/stdc++.h>
#define R register int
#define I inline void
#define lc c[x][0]
#define rc c[x][1]
using namespace std;
const int N = 6e5+100;
int c[N][2],f[N],mi[N],val[N],r[N],st[N],siz[N];
//vis数组对于删过的边标记一下 以免重复删
int n,m,k;
inline bool nroot(R x){
return c[f[x]][0]==x||c[f[x]][1]==x;
}
inline int in(){
int w=0,x=0;char c=0;
while(c<'0'||c>'9') w|=c=='-',c=getchar();
while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return w?-x:x;
}
I pushr(R x){
swap(lc,rc);
r[x]^=1;
}
I pushup(R x){
mi[x]=x;
if(val[mi[lc]]<val[mi[x]]) mi[x]=mi[lc];
if(val[mi[rc]]<val[mi[x]]) mi[x]=mi[rc];
siz[x]=siz[lc]+siz[rc]+(x>n);
}
I pushdown(R x){
if(r[x]){
if(lc) pushr(lc);
if(rc) pushr(rc);
r[x]=0;
}
}
I rotate(R x){
R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][k^1];
if(nroot(y)) c[z][c[z][1]==y]=x; c[x][k^1]=y;c[y][k]=w;
if(w) f[w]=y; f[x]=z;f[y]=x;
pushup(y);
}
I splay(R x){
R y=x,z=0;
st[++z]=y;
while(nroot(y)) st[++z]=y=f[y];
while(z) pushdown(st[z--]);
while(nroot(x)){
y=f[x],z=f[y];
if(nroot(y))
rotate((c[y][1]==x)^(c[z][1]==y)?x:y);
rotate(x);
}
pushup(x);
}
I access(R x){
for(R y=0;x;x=f[y=x])
splay(x),rc=y,pushup(x);
}
inline int findroot(R x){
access(x);splay(x);
while(lc) pushdown(x),x=lc;
splay(x);
return x;
}
I makeroot(R x){
access(x);splay(x);
pushr(x);
}
I split(R x,R y){
makeroot(x);
access(y);splay(y);
}
inline bool judge(R x,R y){
makeroot(x);
if(findroot(y)==x) return true;
return false;
}
I link(R x,R y){
makeroot(x);
f[x]=y;
}
I cut(R x,R y){
makeroot(x);
if(findroot(y)==x&&c[y][0]==0&&f[y]==x){
f[y]=c[x][1]=0;
pushup(x);
}
}
struct edge{
int x,y,st,ed;
}e[N];
vector<int>ins[N],out[N];
int vis[N];
int main(){
n=in(),m=in(),k=in();
e[0].ed=1e9;//这里也要注意 是和自环相关的
for(int i = 0; i <= n; i++) val[i]=1e9;
for(int i = 1; i <= m; i++){
e[i].x=in(),e[i].y=in(),e[i].st=in(),e[i].ed=in();
val[i+n]=e[i].ed;
mi[i+n]=i+n;
siz[i+n]=1;
ins[e[i].st].push_back(i);
out[e[i].ed].push_back(i);
}
int now = 0;
for(int i = 0; i < k; i++){
int v;
for(int j = 0,s = out[i].size(); j < s; j++){
v=out[i][j];
if(!vis[v]) vis[v]=1,cut(v+n,e[v].x),cut(v+n,e[v].y);
}
for(int j = 0,s = ins[i].size(); j < s; j++){
v=ins[i][j];
//printf("i=%d j=%d v=%d\n",i,j,v);
int x = e[v].x,y = e[v].y;
if(judge(x,y)){
split(x,y);
int g = mi[y]-n;
if(x==y) g=0;//自环(不写这句真的要命)
// printf("mi[y]=%d g=%d\n",mi[y],g);
if(e[v].ed<e[g].ed) g=v;
if(siz[y]%2==0) now=max(e[g].ed,now);
vis[g]=1;
if(g!=v){
cut(g+n,e[g].x);cut(g+n,e[g].y);
link(v+n,e[v].x);link(v+n,e[v].y);
}
}else link(v+n,e[v].x),link(v+n,e[v].y);
}
if(i<now) printf("No\n");
else printf("Yes\n");
}
return 0;
}
/*
1 1 5
1 1 1 4
*/