模板1:缩点
P3387 【模板】缩点 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
Tarjan算法+利用并查集维护
主要代码:
void dfs(int u){
for(int i = head[u]; i; i = e[i].nt){
int v = e[i].v;
if(!vis[v]){//如果没搜索
vis[v] = vis[u] + 1;//记录“深度”
dfs(v);//继续搜
}
int fu = fd(u), fv = fd(v);//fd 是并查集函数
if(vis[fv]) vis[fu]<vis[fv]?f[fv]=fu:f[fu]=fv;//深度小的为祖先节点
}
}
vis[u] = -1;//标记搜索完
return;
}
模板2:割点
P3388 【模板】割点(割顶) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
Tarjan算法
首先选定一个根节点,从该根节点开始遍历整个图(使用DFS)。
对于根节点,判断是不是割点很简单——计算其子树数量,如果有2棵即以上的子树,就是割点。因为如果去掉这个点,这两棵子树就不能互相到达。
对于非根节点,判断是不是割点就有些麻烦了。我们维护两个数组dfn[]和low[],dfn[u]表示顶点u第几个被(首次)访问,low[u]表示顶点u及其子树中的点,通过非父子边(回边),能够回溯到的最早的点(dfn最小)的dfn值(但不能通过连接u与其父节点的边)。对于边(u, v),如果low[v]>=dfn[u],此时u就是割点。
主要代码:
void tarjan(int u)
{
dfn[u]=low[u]=++tot;
int ts=0;
for(int i=h[u];i;i=e[i].nt)
{
int v=e[i].to;
if(!dfn[v]){
ts++;
f[v]=u;
tarjan(v);
low[u]=min(low[v],low[u]);
if (!f[u]&&ts>1)
{
mp[u]++;
}else if (f[u]&&low[v]>dfn[u])
{
mp[u]++;
}
}else if(f[u]!=v)
{
low[u]=min(low[u],dfn[v]);
}
}
}
模板3:出入度的利用
P2746 [USACO5.3]校园网Network of Schools - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
有向图,已知n个点m条边
问题一:最少几个点可以遍历全图?
问题二:最少加几条路可以变成强连通图(图上任意俩点互通)?
问题一解决:入度为0的点的个数;
问题二解决:入度为0的点与出度为0的点的最大值;
如图:
此时最大值为出度为0的点个数3,最少加3条边即可
使用构图网站:Graph Editor (csacademy.com)
p2746代码(小心图上只有一个强连通图):
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
#define PI 3.1415
#define ll long long
#define mod 1000000007
using namespace std;
int n,f[105],h[105],cnt,dth[105];
int chu[105],ru[105],vis[105],sm1,sm2;
map<int,int>mp;
struct zz
{
int u,to,nt;
}e[10005];
int fd(int x)
{
if(x!=f[x])f[x]=fd(f[x]);
return f[x];
}
void add(int u,int v)
{
e[++cnt]=zz{u,v,h[u]};
h[u]=cnt;
}
void tarjan(int u)
{
for(int i=h[u];i;i=e[i].nt)
{
int v=e[i].to;
if (!dth[v])dth[v]=dth[u]+1,tarjan(v);
int fu=fd(u),fv=fd(v);
if(dth[fv]>0)dth[fu]<dth[fv]?f[fv]=fu:f[fu]=fv;
}
dth[u]=-1;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
f[i]=i;
int x;
cin>>x;
while (x)
{
add(i,x);
cin>>x;
}
}
for(int i=1;i<=n;i++)
if(!dth[i])dth[i]=1,tarjan(i);
for(int i=1;e[i].u!=0;i++)
{
int u=e[i].u,v=e[i].to;
if(f[u]!=f[v]){
chu[f[u]]++,ru[f[v]]++;
}
}
for(int i=1;i<=n;i++)
{
mp[f[i]]++;
if(f[i]!=i)continue;
if(!chu[i])sm1++;
if(!ru[i])sm2++;
}
cout<<sm2<<endl<< ((mp.size()>1)?max(sm2,sm1):0);
}