BZOJ 4025 二分图 LCT

题意:链接

方法: LCT

解析:

闲来无事随机了一道题,看完题后,我靠这上LCT不就能搞?

随机都能随机个LCT,简直。

然后就快快乐乐地写了个维护删除时间的最大生成树。

WA..

噢难道没这么水!一定我想少了。

等等二分图是啥玩意来着。

二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。

等等也就是这玩意可以有环?

woc画风不对啊,赶紧百度一些关于二分图的东西。

然后看到没有奇环这个定义?

奇环是啥?我只知道基环树啊。

然后宋爷告诉我:奇环不就是有奇数个点的环么….

对啊!=-=

智商下线。

既然二分图不能有奇环,这是显然的嘛!就是从另一个集合跑这个环总会连会自己的集合,这显然不是二分图么。

那维护就得多一项咯?

连边的时候,如果这是自环,显然不行,扔到一个数组里,代表这边存在的时候不能是二分图。

如果不产生环,直接连。

如果产生环

先看这个边的删除时间是否要我们添到树里,如果删除时间较环上删除时间最小的边要小,那么看产生的是奇环还是偶环,奇环则扔到数组里。

如果比环上删除时间最小的边要大,则判断下是奇环还是偶环,奇环则把原来的边扔到数组里,接着就D原来的边连上新的边。

删除边的话,就看这个边在哪里,如果在树上直接删,不影响答案,如果在数组里就把它扔辣就行辣。

枚举每个时间的时候,把这个时间的操作都做完后,看目前的那个数组是否为空,如果为空则为二分图,不为空则不是二分图。

然而这题可以用分治加并查集?挖坑。

Update::15.11.12
分治并查集的思想主要就是按照时间分治,
把每条边存在的时间分成在时间轴上的logn段,动态添加即可,同时需要注意回溯的时候需要把之前做的操作还原,具体实现是采用压栈记录上次栈底的方式。

代码:

LCT

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 200010
#define N 300010
using namespace std;
int n,m,t;
struct node
{
    int x,y,a,b,deltag,no; 
}l[M],l2[M];
int fa[N],ch[N][2],mn[N],val[N],rt[N],rev[N],siz[N];
void pushup(int x)
{
    if(!x)return;
    mn[x]=x;
    siz[x]=x>n;
    if(ch[x][0]!=0&&val[mn[x]]>val[mn[ch[x][0]]])mn[x]=mn[ch[x][0]];
    if(ch[x][1]!=0&&val[mn[x]]>val[mn[ch[x][1]]])mn[x]=mn[ch[x][1]];
    if(ch[x][0]!=0)
        siz[x]+=siz[ch[x][0]];
    if(ch[x][1]!=0)
        siz[x]+=siz[ch[x][1]];
}
void reverse(int x)
{
    if(!x)return ;
    swap(ch[x][0],ch[x][1]);
    rev[x]^=1;
}
void pushdown(int x)
{
    if(!x)return;
    if(rev[x])
    {
        reverse(ch[x][0]),reverse(ch[x][1]);
        rev[x]=0; 
    }
}
void down(int x)
{
    if(!rt[x])down(fa[x]);
    pushdown(x);
}
void rotate(int x)
{
    int y=fa[x],kind=ch[y][1]==x;
    ch[y][kind]=ch[x][!kind];
    fa[ch[y][kind]]=y;
    ch[x][!kind]=y;
    fa[x]=fa[y];
    fa[y]=x;
    if(rt[y])rt[y]=0,rt[x]=1;
    else ch[fa[x]][ch[fa[x]][1]==y]=x;
    pushup(y);
}
void splay(int x)
{
    down(x);
    while(!rt[x])
    {
        int y=fa[x],z=fa[y];
        if(rt[y])rotate(x);
        else if((ch[y][1]==x)==(ch[z][1]==y))rotate(y),rotate(x);
        else rotate(x),rotate(x);
    }
    pushup(x); 
}
void access(int x)
{
    int y=0;
    while(x)
    {
        splay(x);
        rt[ch[x][1]]=1,rt[y]=0;
        ch[x][1]=y;
        pushup(x);
        y=x,x=fa[x];
    }
}
void movetoroot(int x)
{
    access(x);
    splay(x);
    reverse(x);
}
void link(int x,int y)
{
    movetoroot(x);
    fa[x]=y;
}
void cut(int x,int y)
{
    movetoroot(x);
    access(y);
    splay(y);
    ch[y][0]=0;
    fa[x]=0;
    rt[x]=1; 
}
int find_root(int x)
{
    access(x),splay(x);
    while(ch[x][0])x=ch[x][0];
    return x;
}
int head[N],head2[N];
struct node2
{
    int from,to,next;
}edgead[N],edgede[N];
int cnt,cnt2;
void init()
{
    memset(head,-1,sizeof(head));
    memset(head2,-1,sizeof(head2));
    cnt=cnt2=1; 
}
void edgeadd(int from,int to)
{
    edgead[cnt].from=from,edgead[cnt].to=to;
    edgead[cnt].next=head[from];
    head[from]=cnt++;
} 
void edgeadd2(int from,int to)
{
    edgede[cnt2].from=from,edgede[cnt2].to=to;
    edgede[cnt2].next=head2[from];
    head2[from]=cnt2++; 
}
int onthetree[N],inthearr[N];
int intotal;
int main()
{
    scanf("%d%d%d",&n,&m,&t);
    init();
    memset(val,0x3f,sizeof(val));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&l[i].x,&l[i].y,&l[i].a,&l[i].b);
        edgeadd(l[i].a,i),edgeadd2(l[i].b,i);
        val[i+n]=l[i].b,mn[i+n]=i+n;
    }
    for(int i=1;i<=n;i++)rt[i]=1;
    for(int i=1;i<=m;i++)rt[i+n]=1,siz[i+n]=1;
    for(int T=0;T<t;T++)
    {
        for(int i=head[T];i!=-1;i=edgead[i].next)
        {
            int to=edgead[i].to;
            int x=l[to].x,y=l[to].y;
            if(x==y)inthearr[to]=1,intotal++; 
            else if(find_root(x)!=find_root(y))
            {
                onthetree[to]=1,link(x,to+n),link(to+n,y);
            }else
            {
                movetoroot(x),access(y),splay(y);
                int tmp=mn[y]-n;
                if(l[tmp].b<l[to].b)
                {
                    if(!(siz[y]&1))inthearr[tmp]=1,intotal++;
                    cut(l[tmp].x,tmp+n),cut(tmp+n,l[tmp].y);
                    link(x,to+n),link(to+n,y);
                    onthetree[tmp]=0,onthetree[to]=1;
                }else if(!(siz[y]&1))inthearr[to]=1,intotal++;
            }
        }
        for(int i=head2[T];i!=-1;i=edgede[i].next)
        {
            int to=edgede[i].to;
            if(onthetree[to])
            {
                cut(l[to].x,to+n),cut(to+n,l[to].y);
            }else if(inthearr[to])intotal--;
        }
        if(intotal)puts("No");
        else puts("Yes"); 
    }
} 

分治+并查集

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100100
using namespace std;
int n,m,T;
int rnk[N];
int fa[N];
int a[N];
int sta[N<<2];
int top;
struct Edge
{
    int u,v,st,ed;
    Edge(int _u,int _v,int _st,int _ed)
        :u(_u),v(_v),st(_st),ed(_ed){};
};
int find(int x)
{
    while(x!=fa[x])
        x=fa[x];
    return fa[x]=x;
}
int get_distance(int x)
{
    int ret=0;
    while(x!=fa[x]&&fa[x])
        ret^=a[x],x=fa[x];
    return ret;
}
void Union(int x,int y,int z)
{
    int fx=find(x),fy=find(y);
    if(fx==fy)return;
    if(rnk[fx]>rnk[fy])
    {
        fa[fy]=fx;
        a[fy]=z;
        sta[++top]=fy;
    }else if(rnk[fy]>rnk[fx])
    {
        fa[fx]=fy;
        a[fx]=z;
        sta[++top]=fx;
    }else
    {
        rnk[fx]++;
        fa[fy]=fx;
        a[fy]=z;
        sta[++top]=-fx;
        sta[++top]=fy;
    }
}
void declear(int bot)
{
    while(top>bot)
    {
        int tmp=sta[top--];
        if(tmp<0)
        {
            rnk[-tmp]--;
        }else
        {
            fa[tmp]=tmp,a[tmp]=0;
        }
    }
}
vector <Edge> vec;
void Divide_and_Conquer(int L,int R,vector <Edge>& ve)
{
    vector <Edge> :: iterator it;
    vector <Edge> l,r;
    if(L==R&&L==1)
    {
        top++;
        top--;
    }
    int bot=top;
    int mid=(L+R)>>1;
    for(it=ve.begin();it!=ve.end();it++)
    {
        if(it->st==L&&it->ed==R)
        {
            int fx=find(it->u),fy=find(it->v);
            int dis=get_distance(it->u)^get_distance(it->v)^1;
            if(fx!=fy)
            {
                Union(fx,fy,dis);
            }else
            {
                if(dis&1)
                {
                    for(int i=L;i<=R;i++)
                    {
                        puts("No");
                    }
                    declear(bot);
                    return;
                }
            }
        }else if(it->ed<=mid)
            l.push_back(*it);
         else if(it->st>mid)
            r.push_back(*it);
         else
         {
            l.push_back(Edge(it->u,it->v,it->st,mid));
            r.push_back(Edge(it->u,it->v,mid+1,it->ed));
         }
    }
    if(L==R)
    {
        puts("Yes");
    }else
        Divide_and_Conquer(L,mid,l),Divide_and_Conquer(mid+1,R,r);
    declear(bot);
}
int main()
{
    scanf("%d%d%d",&n,&m,&T);
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int u,v,st,ed;
        scanf("%d%d%d%d",&u,&v,&st,&ed);
        st++;
        if(st > ed)
            continue;
        vec.push_back(Edge(u,v,st,ed));
    }
    Divide_and_Conquer(1,T,vec);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值