bzoj4025: 二分图【LCT维护生成树】

本文介绍了一种使用LCT(Link-Cut Tree)解决动态二分图问题的方法,通过动态维护最大生成树来判断在不同时间段内图是否保持二分图特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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。

解题思路:

CDQ分治太高端,还是LCT好想一点。

一个图是二分图的充要条件:没有奇环。

关于环的问题一般要转化为生成树的问题,一条非树边就是一个简单环,即考虑非树边的影响。

而一个奇环的影响仅取决于环上删除时间最小的边,所以我们相当于要动态维护当前时刻关于删除时间的最大生成树,上LCT。

对于每个时刻,加入一条边时,若它为树边直接加,若形成环,则弹出环上删除时间最小的边,若同时为奇环,则将最小边加入一个集合,表示这条边存在时,图中总有奇环(偶环不用加这条边,因为若之后加入的边与这条边形成了奇环,那么新边一定能与其他边构成奇环)。

删去一条边时,若为树边直接删,就若在集合中,在集合中删去。
这样,在每一时刻,若集合大小为0则图为二分图。

注意LCT维护生成树不是把边权附到点上,而是把边也看做点向两个端点连边。那么边的sz为0,权值为删除时间;点sz为1,权值为正无穷。

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=500005,INF=10000000;
struct edge{int x,y,st,ed;}bian[N];
struct node{int t,id;}e1[N],e2[N];
inline bool cmp(const node &a,const node &b){return a.t<b.t;}
int n,m,T,cnt,top;
int fa[N],son[N][2],id[N],mn[N],val[N],size[N],rev[N],stk[N];
bool ontree[N],in[N];

int which(int x){return son[fa[x]][1]==x;}
bool rt(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
int Min(int x,int y){return bian[x].ed<bian[y].ed?x:y;}
void update(int x)
{
    size[x]=size[son[x][0]]+size[son[x][1]]+val[x];
    mn[x]=Min(id[x],Min(mn[son[x][0]],mn[son[x][1]]));
}
void pushdown(int x)
{
    swap(son[x][0],son[x][1]);
    rev[x]=0;
    if(son[x][0])rev[son[x][0]]^=1;
    if(son[x][1])rev[son[x][1]]^=1;
}
int rotate(int x)
{
    int y=fa[x],z=fa[y],t=which(x);
    if(!rt(y))son[z][which(y)]=x;
    fa[x]=z,fa[y]=x;
    son[y][t]=son[x][t^1],son[x][t^1]=y;
    if(son[y][t])fa[son[y][t]]=y;
    update(y),update(x);
}
void splay(int x)
{
    stk[top=1]=x;
    for(int i=x;!rt(i);i=fa[i])stk[++top]=fa[i];
    for(int i=top;i;i--)if(rev[stk[i]])pushdown(stk[i]);
    while(!rt(x))
    {
        if(!rt(fa[x]))
            which(x)==which(fa[x])?rotate(fa[x]):rotate(x);
        rotate(x);
    }
}
void access(int x)
{
    for(int y=0;x;y=x,x=fa[x])
    {
        splay(x),son[x][1]=y,update(x);
        if(y)fa[y]=x;
    }
}
int findroot(int x)
{
    access(x),splay(x);
    while(son[x][0])x=son[x][0];
    return x;
}
void makeroot(int x)
{
    access(x),splay(x),rev[x]^=1;
}
void link(int x,int y)
{
    makeroot(x),fa[x]=y;
}
void cut(int x,int y)
{
    makeroot(x),access(y),splay(y);
    fa[x]=son[y][0]=0;
}

int main()
{
    //freopen("lx.in","r",stdin);
    //freopen("lx.out","w",stdout);
    n=getint(),m=getint(),T=getint();
    bian[0].ed=INF;
    for(int i=1;i<=n;i++)val[i]=1;
    for(int i=1;i<=m;i++)
    {
        bian[i].x=getint(),bian[i].y=getint();
        bian[i].st=e1[i].t=getint(),bian[i].ed=e2[i].t=getint();
        e1[i].id=e2[i].id=id[i+n]=mn[i+n]=i;
    }
    sort(e1+1,e1+m+1,cmp),sort(e2+1,e2+m+1,cmp);
    for(int t=0,i=1,j=1;t<T;t++)
    {
        while(e1[i].t<=t&&i<=m)
        {
            int e=e1[i].id,x=bian[e].x,y=bian[e].y;
            if(findroot(x)!=findroot(y))link(x,e+n),link(e+n,y),ontree[e]=1;
            else
            {
                makeroot(x),access(y),splay(y);
                int tmp=mn[y],sz=size[y];
                if(bian[tmp].ed<bian[e].ed)
                {
                    cut(bian[tmp].x,tmp+n),cut(tmp+n,bian[tmp].y),ontree[tmp]=0;
                    link(x,e+n),link(e+n,y),ontree[e]=1;
                    e=tmp;
                }
                if(sz&1)cnt++,in[e]=1;
            }
            i++;
        }
        while(e2[j].t<=t&&j<=m)
        {
            int e=e2[j].id,x=bian[e].x,y=bian[e].y;
            if(ontree[e])cut(x,e+n),cut(e+n,y),ontree[e]=0;
            if(in[e])cnt--,in[e]=0;
            j++;
        }
        cnt?puts("No"):puts("Yes");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值