\(Tarjan\)&\(2-SAT\)

标签: 知识点总结

安利XZYXZY

ps:里面的部分东西来自\(Anson\)和\(yler\)和\(XZY\)

阅读体验:https://zybuluo.com/Junlier/note/1293491

\(Tarjan\)大爷

前世没见过Tarjan这么牛逼的人

并且他还弄了好多别的东西。。。

留这么多东西给我们。。。爆炸

强连通分量&割点&割边&点双&边双

简介

在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。

若一个无向图中的去掉任意一个节点(一条边)都不会改变此图的连通性,即不存在割点(桥),则称作点(边)双连通图。一个无向图中的每一个极大点(边)双连通子图称作此无向图的点(边)双连通分量。

一些板子

大道理就不讲了,别人讲的好得多

强连通分量

void Tarjan(int now)
{
dfn[now]=low[now]=++Dex;
in[now]=true,st[++top]=now;
for(int i=hd[now];i;i=ljl[i].nxt)
{
int qw=ljl[i].to;
if(!dfn[qw])Tarjan(qw),low[now]=min(low[now],low[qw]);
else if(in[qw])low[now]=min(low[now],dfn[qw]);
}
if(dfn[now]==low[now])
{
++color;
while(st[top+1]!=now)
{
col[st[top]]=color;
in[st[top]]=false,--top;
}
}
}

割边(桥)

为树边且连接的子树返祖不回来

int tarjan(int now,int fm)
{
low[now]=dfn[now]=++Dex;
for(int i=hd[now];i;i=ljl[i].nxt)
{
int qw=ljl[i].to;
if(qw==fm)continue;
if(!dfn[qw])
{
Tarjan(qw,now);
low[now]=min(low[now],low[qw]);
if(low[qw]>dfn[now])
Bridge.pk(mp(min(now,qw),max(now,qw)));
// push_back make_pair
}
else if(dfn[qw]<dfn[now])low[now]=min(low[now],dfn[qw]);
}
}

割点

  1. 为树根时:两个以上的子树就\(ok\)
  2. 不为树根时:子树返祖最多到自己
void Tarjan(int now,int rt)
{
int s=0;dfn[now]=low[now]=++Dex;
for(int i=hd[now];i;i=ljl[i].nxt)
{
int qw=ljl[i].to;
if(!dfn[qw])
{
Tarjan(qw,rt);low[now]=min(low[now],low[qw]);
if(now==rt)++s;
else if(low[qw]>=dfn[now])cut[now]=1;
}
else low[now]=min(low[now],dfn[qw]);
}
if(now==rt&&s>1)cut[now]=1;
}

点双

  1. 求割点
  2. 重新\(Dfs\)一遍图,强制不经过割点,栈里面就是了(要包含割点)
  3. 所以,一个割点会在多个点双里面
void Tarjan(int now,int fm)
{
int s=0;dfn[now]=low[now]=++Dex;
for(int i=hd[now];i;i=ljl[i].nxt)
{
int qw=ljl[i].to;if(qw==fm)continue;
if(!dfn[qw])
{
Tarjan(qw,now);s++;
tag[now]|=low[qw]>=dfn[now];
low[now]=min(low[now],low[qw]);
}
else low[now]=min(low[now],dfn[qw]);
}
if(!fm&&s==1)tag[now]=0;
}

边双

求出桥,然后重新Dfs一遍,强制不经过桥,所有的联通块都是边双。。。

\(Brg\)代表边是不是桥,\(col\)之类的就是边双染色了。。。

void Tarjan(int now,int fm)
{
dfn[now]=low[now]=++Dex;
for(int i=G1.hd[now];i;i=G1.ljl[i].nxt)
{
int qw=G1.ljl[i].to;
if(qw==fm)continue;
if(!dfn[qw])
{
Tarjan(qw,now);
low[now]=min(low[now],low[qw]);
if(low[qw]>dfn[now])Brg[i>>1]=1;
}
else low[now]=min(low[now],dfn[qw]);
}
}
void Dfs(int now,int Col)
{
col[now]=Col;
for(int i=G1.hd[now];i;i=G1.ljl[i].nxt)
{
int qw=G1.ljl[i].to;
if(!Brg[i>>1]&&!col[qw])Dfs(qw,Col);
}
}

题目

缩点(其实可以放一起)

简介

对于一些题目,我们可以在求出 强/点双/边双 连通分量后将每个东西(你懂的)缩成一个点,从而把图简化。有向图缩强连通分量后会变成一个DAG,边双缩点变成树,至于点双。。。需要用一种叫做圆方树的东西维护。

题目

\(Tarjan\)求\(LCA\)

这是一个询问\(O(1)\)但是只能离线的求\(LCA\)的高科技

我们求\((p,q)\)的\(LCA\)把询问离线,在\(p,q\)上对询问挂链

扫一遍整棵树,一边回答

那么具体怎么回答的呢,维护一个并查集,表示已经完成的子树的最高祖先

这个具体来说就是\(fa[i]\)为\(i\)点在已经完成的树内的最高祖先,显然对于其他没有完成的点如果要求与\(i\)的\(LCA\)的话,就直接是\(fa[i]\)了是吧

反正很难懂,建议自己对这代码手玩,例题:洛谷P3379 【模板】最近公共祖先(LCA)

//你不觉得压行很美丽吗QwQ
#include<bits/stdc++.h>
#define il inline
#define rg register
#define ldb double
#define lst long long
#define rgt register int
#define N 500050
#define pb push_back
#define qw ljl[i].to
#define ot Pm[now][i].to
using namespace std;
const int Inf=1e9;
il int MAX(rgt x,rgt y){return x>y?x:y;}
il int MIN(rgt x,rgt y){return x<y?x:y;}
il int read()
{
int s=0,m=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')m=1;ch=getchar();}
while( isdigit(ch))s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return m?-s:s;
} int n,m,Rt,cnt;
int hd[N],fa[N],vis[N];
struct NODE{int to,id;};
vector<NODE> Pm[N];int ans[N];
struct EDGE{int to,nxt;}ljl[N<<1];
int Find_fa(rgt k){return fa[k]==k?k:fa[k]=Find_fa(fa[k]);}
il void Add(rgt p,rgt q){ljl[++cnt]=(EDGE){q,hd[p]},hd[p]=cnt;} void Answer(rgt now)
{
for(rgt i=0,Sz=Pm[now].size();i<Sz;++i)
if(vis[ot])ans[Pm[now][i].id]=Find_fa(ot);
}
void Dfs(rgt now)
{
vis[now]=1,Answer(now);
for(rgt i=hd[now];i;i=ljl[i].nxt)
if(!vis[qw])Dfs(qw),fa[qw]=now;
} int main()
{
n=read(),m=read(),Rt=read();
for(rgt i=1;i<=n;++i)fa[i]=i;
for(rgt i=1,p,q;i<n;++i)
p=read(),q=read(),Add(p,q),Add(q,p);
for(rgt i=1,u,v;i<=m;++i){
u=read(),v=read();
Pm[u].pb((NODE){v,i}),Pm[v].pb((NODE){u,i});
}Dfs(Rt);for(rgt i=1;i<=m;++i)printf("%d\n",ans[i]);
return 0;
}

2-SAT

简介

每种物品有选或者不选两种状态,有些限制条件形如

选了则必须选,和不能同时选,必须选等等

把逻辑限制关系变成连边

a->b表示如果成立那么一定成立

这个要求你理解逆否命题

逆否命题,举个例子,选必须选,那么我选了就不能选,选就必须选

由于逆否命题产生的对称性使得问题得以在时间求解

具体来说要求同时连接x->y y'->x'

这样跑一遍缩点后如果统一物品的两种状态在同一个边双连通分量中就无解

否则可以输出方案,具体来说是每个点选择缩成的超级点中编号最小的那个(也就是反图拓扑序最小的那个)

题目

综合题目?

Tarjan&2-SAT 总结的更多相关文章

  1. 学习笔记(two sat)

    关于two sat算法 两篇很好的论文由对称性解2-SAT问题(伍昱), 赵爽 2-sat解法浅析(pdf). 一些题目的题解 poj 3207 poj 3678 poj 3683 poj 3648 ...

  2. HIT 1917 2—SAT

    题目大意:一国有n个党派,每个党派在议会中都有2个代表, 现要组建和平委员会,要从每个党派在议会的代表中选出1人,一共n人组成和平委员会. 已知有一些代表之间存在仇恨,也就是说他们不能同时被选为和平委 ...

  3. HDU4738 tarjan割边|割边、割点模板

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=4738 坑点: 处理重边 图可能不连通,要输出0 若求出的结果是0,则要输出1,因为最少要派一个人 #inc ...

  4. bzoj 1179[Apio2009]Atm (tarjan+spfa)

    题目 输入 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一 ...

  5. tarjan讲解(用codevs1332(tarjan的裸题)讲解)

    主要借助这道比较裸的题来讲一下tarjan这种算法 tarjan是一种求解有向图强连通分量的线性时间的算法.(用dfs来实现) 如果两个顶点可以相互通达,则称两个顶点强连通.如果有向图G的每两个顶点都 ...

  6. NOIP2009最优贸易[spfa变形|tarjan 缩点 DP]

    题目描述 C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市.任意两个 城市之间最多只有一条道路直接相连.这 m 条道路中有一部分为单向通行的道路,一部分 为双向通行的道路 ...

  7. Tarjan

    //求强连通分量 void uni(int x,int y){ if (rank[x]<rank[y]){ fa[x]=y; size[y]+=size[x]; }else{ rank[x]+= ...

  8. 【UOJ#67】新年的毒瘤 Tarjan 割点

    #67. 新年的毒瘤 UOJ直接黏贴会炸...    还是戳这里吧: http://uoj.ac/problem/67#tab-statement Solution 看到这题的标签就进来看了一眼. 想 ...

  9. 【Codefoces487E/UOJ#30】Tourists Tarjan 点双连通分量 + 树链剖分

    E. Tourists time limit per test: 2 seconds memory limit per test: 256 megabytes input: standard inpu ...

  10. 【BZOJ-1123】BLO Tarjan 点双连通分量

    1123: [POI2008]BLO Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 970  Solved: 408[Submit][Status][ ...

随机推荐

  1. 查询sql server占用内存的情况

    se master go select * from sysperfinfo where counter_name like '%Total Server Memory (KB)%' go

  2. linux ab压力测试

    1.安装 yum -y install httpd-tools 2.检测版本 ab -V 3.常用 ab -c -n 127.0.0.1/index.php #同时处理100个请求并运行10次inde ...

  3. [sqlmap源码阅读] 数据库识别

    通过网页返回的数据库错误信息识别网站所有数据库类型,用到的正则表达式及支持识别的数据库类型,这些信息以xml文件的形式存在,使用 sax 解析xml.

  4. SpringMVC的@ResponseBody注解简介

    SpringMVC简介 SpringMVC也叫Spring Web MVC 属于展示层框架.是Spring框架的一部分. 核心组件类DispatherServlet springMVC是围绕Dispa ...

  5. python数据分析第二版:numpy

    一:Numpy # 数组和列表的效率问题,谁优谁劣 # 1.循环遍历 import numpy as np import time my_arr = np.arange(1000000) my_lis ...

  6. Spring Aop 动态代理失效分析

    1. Spring Aop 原理 Spring Aop 通过动态代理创建代理对象,在调用代理对象方法前后做增强. 2. Transactional, Async 注解失效? 当在动态代理方法中调用当前 ...

  7. 【leetcode】486. Predict the Winner

    题目如下: Given an array of scores that are non-negative integers. Player 1 picks one of the numbers fro ...

  8. 【leetcode】1048. Longest String Chain

    题目如下: Given a list of words, each word consists of English lowercase letters. Let's say word1 is a p ...

  9. 【leetcode】813. Largest Sum of Averages

    题目如下: 解题思路:求最值的题目优先考虑是否可以用动态规划.记dp[i][j]表示在数组A的第j个元素后面加上第i+1 (i从0开始计数)个分隔符后可以得到的最大平均值,那么可以得到递归关系式: d ...

  10. ASP 转换HTML特殊字符

    Function HtmlDecode(ByVal s) If Has(s) Then s = regReplace(s, "<br\s*/?\s*>", vbCrLf ...