P5787 二分图 /线段树分治 或 lct

没有奇环的图就是二分图,所以我们要想办法去维护一个图里面有没有二分图。

线段树分治的做法:以时间建立线段树,对于每一条边的出现与消失时间,我们通过区间更新把这条边挂在线段树的结点上,最后对线段树进行一次遍历。搜索的时候加边,回溯撤销边。用扩展域并查集判断是否为二分图,因为要撤销边,所以并查集得是可撤销的,并且不能路径压缩,同时为了保证复杂度必须按秩合并。

并查集判断二分图的方法是 对于每一条加入的边 (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
*/

猜你喜欢

转载自blog.csdn.net/weixin_43824564/article/details/106679803
今日推荐