bzoj4025 二分图(线段树分治+带权并查集维护路径长奇偶性)

83 篇文章 0 订阅

bzoj4025 二分图

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=4025

题意:
神犇有一个n个节点的图。因为神犇是神犇,所以在T时间内一些边会出现后消失。神犇要求出每一时间段内这个图是否是二分图。

数据范围
n<=100000,m<=200000,T<=100000,1<=u,v<=n,0<=start<=end<=T。

题解:
判断是否是二分图即判断是否没有奇环。

考虑按st顺序一条一条地加边,如果加入一条边,形成偶环,那么对后面的边没有影响,
如果形成奇环,就不是二分图,但是维护一个图显然不好维护,
那么好维护的方式就是转化成树,
形成环时,如果删掉这个环上的某一条边,就还是树,
为了好维护,选择弹掉ed最小的那条边。那么如果成奇环,就是在这个ed最小的边未消失之前都不是二分图。
如果后面的某条边本来会与此条目前被弹掉的边成奇环,而删掉这条边则不能成环或是成偶环,
那么前者不可能发生,因为只有原来那条边会成环才不会加,
后者则原来那条边成奇环,在统计贡献时已经覆盖了此条边在其中会成奇环的时刻。
于是就是维护按ed的最大生成树,用LCT。

但是其实可以按照线段树分治的思想,每条边存在的区间打上标记,在叶子节点输出答案。
思路与上边相似,只是这次成奇环时没有弹边,直接就这个区间的答案都是No即可。

关于这个并查集维护路径奇偶性的正确性证明见这篇相当详细的题解。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=100005;
const int M=200005;
int n,m,T,S[2*M],top=0,fa[N],rank[N],c[N];
struct node
{
    int u,v,st,ed;
    node(){}
    node(int u,int v,int st,int ed):u(u),v(v),st(st),ed(ed){}
};
int getfa(int x)
{
    if(fa[x]==x) return x;
    else return getfa(fa[x]);
}
int dis(int x)
{
    if(fa[x]==x||!fa[x]) return 0;
    return c[x]^dis(fa[x]);
}
void merge(int x,int y,int w)
{
    if(rank[x]>rank[y]) swap(x,y);
    if(rank[x]==rank[y]) {S[++top]=-y; rank[y]++;}
    S[++top]=x; fa[x]=y; c[x]=w;
}
void res(int x)
{
    while(top>x)
    {
        if(S[top]<0) rank[-S[top]]--;
        else {fa[S[top]]=S[top]; c[S[top]]=0;}
        top--;
    }
}
void query(int lf,int rg,vector<node> &E)
{
    int pre=top; int mid=(lf+rg)>>1;
    vector<node> L,R; int sz=E.size();
    for(int i=0;i<sz;i++)
    {
        if(E[i].st==lf&&E[i].ed==rg)
        {
            int x=E[i].u; int y=E[i].v;
            int fx=getfa(x); int fy=getfa(y);
            if(fx!=fy)
            {
                int w=dis(x)^dis(y)^1;
                merge(fx,fy,w);
            }
            else
            {
                int w=dis(x)^dis(y);
                if((w&1)==0) {for(int j=lf;j<=rg;j++) printf("No\n"); res(pre); return;}
            }
        }
        else
        {
            if(E[i].ed<=mid) L.push_back(E[i]);
            else if(E[i].st>mid) R.push_back(E[i]);
            else {L.push_back(node(E[i].u,E[i].v,E[i].st,mid));
            R.push_back(node(E[i].u,E[i].v,mid+1,E[i].ed));}
        }
    }
    if(lf==rg){printf("Yes\n"); res(pre); return;}
    query(lf,mid,L); query(mid+1,rg,R);
    res(pre);
}
int main()
{
    scanf("%d%d%d",&n,&m,&T);
    for(int i=1;i<=n;i++) fa[i]=i,rank[i]=0;
    vector<node> e;
    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) e.push_back(node(u,v,st,ed));
    }
    query(1,T,e);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值