bzoj 4025 二分图

【题目大意】

  有n个点m条边,边会在start时刻出现在end时刻消失,求对于每一段时间,该图是不是一个二分图。

  判断二分图的一个简单的方法:是否存在奇环

  若存在奇环,就不是二分图。

  假设加入一条u->v的边,u,v已经联通,怎么知道是否是一个奇环呢?只需要知道u,v之间的距离就行了。距离为偶数则是一个奇环。

  路径?加边?删边?

  很容易就想到是LCT。

  维护u->v的距离。

  每次加入一条边,就判断是否先前已经联通,否,则家父,若是,就判断u,v之间的距离。

  假若已经构成一个奇环,那么,在这个环的任意一条边都未被删除前,都不是二分图。

  那就在维护一个minv,表示最小的end值。加入一条边构成了环,就删掉最小end值的那条边,加入新边。

  由于是最小值,删掉不会有影响。(最小的end值的那条边肯定是先消失的)

  要把边拆成点,为了记录min值,以及对应的边。

  细节,慢慢处理就好了。(被坑了好久……)

注意:有自环,需特判。

【附上代码】

#include<cstdio>
#include<algorithm> #define imax(a,b) ((a>b)?(a):(b))
#define imin(a,b) ((a<b)?(a):(b)) using namespace std; const int N=100010;
int n,m,T,cnt,len;
struct tree
{
tree *c[2],*f,*pp,*L,*R;
bool flip;
int minv,val,siz;
int d() { return (f->c[1]==this); }
void sc(tree *x,int d) { (c[d]=x)->f=this; }
} nil[N*3],*ro[N*3],*stack[N*3];
struct data { int u,v,start,end; } d[N<<1];
int ans[N]; tree *newtree()
{
nil[++cnt]=nil[0];
return nil+cnt;
} void down(tree *x)
{
if(x==nil) return;
if(x->flip)
{
x->flip=0;
swap(x->c[0],x->c[1]);
if(x->c[0]!=nil) x->c[0]->flip^=1;
if(x->c[1]!=nil) x->c[1]->flip^=1;
}
} void up(tree *x)
{
if(x==nil) return;
x->minv=x->val; x->siz=1;
if(x->c[0]!=nil) x->minv=imin(x->minv,x->c[0]->minv),x->siz+=x->c[0]->siz;
if(x->c[1]!=nil) x->minv=imin(x->minv,x->c[1]->minv),x->siz+=x->c[1]->siz;
} void rotate(tree *x)
{
int d=x->d();
tree *y=x->f;
y->sc(x->c[!d],d);
if(y->f==nil) x->f=nil; else y->f->sc(x,y->d());
x->sc(y,!d);
up(y); up(x);
x->pp=y->pp;
y->pp=nil;
} void splay(tree *x)
{
int hy=1;
stack[hy]=x;
for(;stack[hy]->f!=nil;hy++) stack[hy+1]=stack[hy]->f;
for(;hy;) down(stack[hy]),hy--;
for(tree *y;x->f!=nil;)
{
y=x->f;
if(y->f!=nil)
(x->d()^y->d())?rotate(x):rotate(y);
rotate(x);
}
} void access(tree *x)
{
tree *y=nil;
while(x!=nil)
{
splay(x);
if(x->c[1]!=nil)
{
x->c[1]->f=nil;
x->c[1]->pp=x;
}
x->c[1]=y;
if(y!=nil)
{
y->f=x;
y->pp=nil;
}
up(x);
y=x; x=x->pp;
}
} void evert(tree *x)
{
access(x);
splay(x);
x->flip^=1;
} void link(tree *x,tree *y)
{
evert(y);
y->pp=x;
} void cut(tree *x,tree *y)
{
evert(x);
access(y);
splay(y);
if(y->c[0]!=nil)
{
y->c[0]->f=nil;
y->c[0]=nil;
}
up(y);
} tree *findroot(tree *x)
{
access(x);
splay(x);
while(x->c[0]!=nil)
{
x=x->c[0];
down(x);
}
splay(x);
return x;
} tree *findmin(tree *x,tree *y)
{
evert(x);
access(y);
splay(x); down(x);
len=(x->siz+1)>>1;
int find=x->minv;
for(;;)
{
if(x->val==find) break;
if(x->c[0]!=nil && x->c[0]->minv==find) x=x->c[0];
else x=x->c[1];
down(x);
}
splay(x);
return x;
} bool cmp(data A,data B) { return (A.start<B.start); } int main()
{
scanf("%d%d%d",&n,&m,&T); cnt=0;
nil->val=nil->minv=1e9; nil->siz=0; nil->pp=nil;
nil->f=nil->c[0]=nil->c[1]=nil->L=nil->R=nil;
for(int i=1;i<=m;i++) scanf("%d%d%d%d",&d[i].u,&d[i].v,&d[i].start,&d[i].end);
sort(d+1,d+1+m,cmp);
for(int i=1;i<=n;i++) ro[i]=newtree();
for(int i=1;i<=m;i++)
{
ro[n+i]=newtree();
ro[n+i]->val=d[i].end; ro[n+i]->minv=d[i].end;
ro[n+i]->L=ro[d[i].u]; ro[n+i]->R=ro[d[i].v];
}
for(int i=1;i<=m;i++)
{
if(d[i].u==d[i].v)
{
ans[d[i].start]++; ans[d[i].end]--;
continue;
}
if(findroot(ro[d[i].u])!=findroot(ro[d[i].v]))
{
link(ro[d[i].u],ro[n+i]);
link(ro[n+i],ro[d[i].v]);
}
else
{
tree *getmin=findmin(ro[d[i].u],ro[d[i].v]);
if(d[i].start<getmin->val && (len&1))
{
ans[d[i].start]++;
ans[imin(getmin->val,d[i].end)]--; //重点啊,我就被这里坑了
}
if(d[i].end>getmin->val)
{
cut(getmin,getmin->L);
cut(getmin,getmin->R);
link(ro[d[i].u],ro[n+i]);
link(ro[n+i],ro[d[i].v]);
}
}
}
int yu=0;
for(int i=0;i<T;i++)
{
yu+=ans[i];
if(yu>0) printf("No\n"); else printf("Yes\n");
}
return 0;
}

【其他思路】

  分治+可回溯并查集(就是不带路径压缩的并查集)

  Solve(L,R,S)表示处理L到R这个时间段,有关联的边集为S。

  如果存在L->R的时间段的边,且加入后构成了奇环,那么L->R这段时间都不为二分图,直接退出。

  把边集划分为左右两边,即边与左区间的交集归左边,右边的交集归右边。(是start和end的交集)

  然后先处理左边,再处理右边就行了。

  具体的看这里,我没写cdq分治,这是我同学的

bzoj 4025 二分图 分治+并查集/LCT的更多相关文章

  1. BZOJ4025 二分图 分治 并查集 二分图 带权并查集按秩合并

    原文链接http://www.cnblogs.com/zhouzhendong/p/8683831.html 题目传送门 - BZOJ4025 题意 有$n$个点,有$m$条边.有$T$个时间段.其中 ...

  2. [BZOJ 4025]二分图(线段树分治+带边权并查集)

    [BZOJ 4025]二分图(线段树分治+带边权并查集) 题面 给出一个n个点m条边的图,每条边会在时间s到t出现,问每个时间的图是否为一个二分图 \(n,m,\max(t_i) \leq 10^5\ ...

  3. bzoj 4025 二分图——线段树分治+LCT

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4025 线段树分治,用 LCT 维护链的长度即可.不过很慢. 正常(更快)的方法应该是线段树分 ...

  4. 2018.09.30 bzoj4025: 二分图(线段树分治+并查集)

    传送门 线段树分治好题. 这道题实际上有很多不同的做法: cdq分治. lct. - 而我学习了dzyo的线段树分治+并查集写法. 所谓线段树分治就是先把操作分成lognlognlogn个连续不相交的 ...

  5. hdu_5354_Bipartite Graph(cdq分治+并查集判二分图)

    题目链接:hdu_5354_Bipartite Graph 题意: 给你一个由无向边连接的图,问对于每一个点来说,如果删除这个点,剩下的点能不能构成一个二分图. 题解: 如果每次排除一个点然后去DFS ...

  6. BZOJ_4025_二分图_线段树按时间分治+并查集

    BZOJ_4025_二分图_线段树按时间分治+并查集 Description 神犇有一个n个节点的图.因为神犇是神犇,所以在T时间内一些边会出现后消失.神犇要求出每一时间段内这个图是否是二分图.这么简 ...

  7. bzoj 3669: [Noi2014]魔法森林(并查集+LCT)

    Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...

  8. 【CF576E】Painting Edges 线段树按时间分治+并查集

    [CF576E]Painting Edges 题意:给你一张n个点,m条边的无向图,每条边是k种颜色中的一种,满足所有颜色相同的边内部形成一个二分图.有q个询问,每次询问给出a,b代表将编号为a的边染 ...

  9. HDU 3081 Marriage Match II (二分图,并查集)

    HDU 3081 Marriage Match II (二分图,并查集) Description Presumably, you all have known the question of stab ...

随机推荐

  1. SQL基本操作——COVERT

    CONVERT() 函数是把日期转换为新数据类型的通用函数.CONVERT() 函数可以用不同的格式显示日期/时间数据. --语法 CONVERT(data_type(length),data_to_ ...

  2. js 攻坚克难

    new new : 官方解释: 如果在一个函数前面带上new来调用,那么背地里将会创建一个连接到该函数的prototype的成员的新对象,同时this会被绑定到哪个新对象上: new 是用来创建对象的 ...

  3. Linux内存压力测试-memtester工具

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  4. xcode构建webdriverAgent时报错Messaging unqualified id的解决办法

    在使用xcode构建webdriverAgent时,提示build failed,报错信息为:semantic issue:Messaging unqualified id,可以参考以下解决方案 xc ...

  5. HTML5轻松实现全屏视频背景

    想在你的网页首页中全屏播放一段视频吗?而这段视频是作为网页的背景,不影响网页内容的正常浏览.那么我告诉你有一款Javascript库正合你意,它就是Bideo.js. 参考网址: https://ww ...

  6. logback日志配置文件

    application.properties application.properties logback-spring.xml <?xml version="1.0" en ...

  7. loader__demo_css

    环境 node + yarn + webpack4.0 + webpack-cli + style-loader css-loader 文件结构 │ package.json │ webpack.co ...

  8. postgres主从配置

    运维开发技术交流群欢迎大家加入一起学习(QQ:722381733) 开始部署postgres主从(如果没不会安装postgres的请去上一个博文中查看) 这里我使用了两台服务器部署 主:192.168 ...

  9. git 的简单使用(5)

    git中打标签非常简单:只需要先奇幻岛想要打标签的分支 然后使用指令 git tag <name>就可以打一个新标签 可以用指令 git tag 查看所有标签 git tag <标签 ...

  10. 远程连接Ubuntu的桌面

    参考:http://www.linuxidc.com/Linux/2016-06/132442.htm http://teliute.org/linux/TeUbt/lesson52/lesson52 ...