老师讲图论-缩点-复习
我想 我没学过 缩点啊 (OTL)
先讲 割点
定义 在无向连通图中,如果将其中一个点以及所有连接该点的边去掉,图就不再连通,那么这个点就叫做割点
主要思想
观察(DFS)搜索树,我们可以发现有两类节点可以成为割点:
-
对根节点(u),若其有两棵或两棵以上的子树,则该根结点u为割点;
-
对非叶子节点(u)(非根节点),若其中的某棵子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与该棵子树的节点不再连通;则节点u为割点。
我们用(dfn[u])记录节点u在DFS过程中被遍历到的次序号,(low[u])记录节点(u)或(u)的子树通过非父子边追溯到最早的祖先节点(即(DFS)次序号最小),那么(low[u])的计算过程如下:
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define reg register int
#define isdigit(x) ('0' <= (x)&&(x) <= '9')
template<typename T>
inline T Read(T Type)
{
T x = 0,f = 1;
char a = getchar();
while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
return x * f;
}
const int MAXN = 20010,MAXM = 200010;
bool vis[MAXN],out[MAXN];
int head[MAXN],cnt,dfn[MAXN],low[MAXN],fa[MAXN],tot,root;
struct node
{
int v,next;
}edge[MAXM];
inline void addedge(int u,int v)
{
edge[++cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
inline void Tarjan(int x)
{
vis[x] = 1,dfn[x] = low[x] = ++tot;
int kid = 0;
for(reg i = head[x];i;i = edge[i].next)
{
int v = edge[i].v;
if(!vis[v])
{
Tarjan(v);
if(x == root)kid++;
fa[v] = x;
low[x] = min(low[x],low[v]);
if(x != root&&low[v] >= dfn[x]) out[x] = 1;
}
low[x] = min(low[x],dfn[v]);
}
if(x == root&&kid >= 2)
out[x] = 1;
}
int main()
{
int n = Read(1),m = Read(1);
for(reg i = 1;i <= m;i++)
{
int u = Read(1),v = Read(1);
addedge(u,v),addedge(v,u);
}
for(reg i = 1;i <= n;i++)
if(!vis[i])
{
root = i;
Tarjan(i);
}
int size = 0;
for(reg i = 1;i <= n;i++) if(out[i]) size++;
printf("%d
",size);
for(reg i = 1;i <= n;i++)
if(out[i]) printf("%d ",i);
return 0;
}
缩点
就将强连通分量的点 缩成一个点
补充:(DAG) 有向无环图
(Tarjan)缩点(+DAGdp)
(DAGdp)的(dp)
如果是这样的(dp[son]=f(dp[fa]))
先要拓扑排序
#include <stack>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define reg register int
#define isdigit(x) ('0' <= x&&x <= '9')
template<typename T>
inline T Read(T Type)
{
T x = 0,f = 1;
char a = getchar();
while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
return x * f;
}
const int MAXN = 1e4 + 10,MAXM = 1e5 + 10;
bool vis[MAXN],instack[MAXN];
stack<int> st;
vector<int> seq,ahead[MAXN],behind[MAXN];
int n,cnt,d,cnt_scc,ans,dp[MAXN],head[MAXN],tot;
struct point
{
int scc,dfn,low,power;
}poi[MAXN];
struct SCC
{
int power,In;
}scc[MAXN];
struct node
{
int u,v,next;
}edge[MAXM];
inline void addedge(int u,int v)
{
edge[++cnt].v = v;
edge[cnt].u = u;
edge[cnt].next = head[u];
head[u] = cnt;
}
inline void tarjan(int x)
{
poi[x].dfn = poi[x].low = ++tot;
vis[x] = 1,st.push(x),instack[x] = 1;
for(reg i = head[x];i;i = edge[i].next)
{
int v = edge[i].v;
if(!vis[v])
{
tarjan(v);
poi[x].low = min(poi[x].low,poi[v].low);
} else if(instack[v]) poi[x].low = min(poi[x].low,poi[v].dfn);
}
if(poi[x].dfn == poi[x].low)
{
scc[++cnt_scc].power = scc[++cnt_scc].In = 0;
while(1)
{
int t = st.top();st.pop();
poi[t].scc = cnt_scc;instack[t] = 0;
scc[cnt_scc].power += poi[t].power;
if(t == x) break;
}
}
return;
}
inline void topo()
{
queue<int> q;
for(reg i = 1;i <= cnt_scc;i++)
if(!scc[i].In) q.push(i);
while(!q.empty())
{
int it = q.front();q.pop();
seq.push_back(it);
for(reg i = 0;i < behind[it].size();i++)
{
int v = behind[it][i];
scc[v].In--;
if(!scc[v].In) q.push(v);
}
}
}
int main()
{
n = Read(1);int m = Read(1);
for(reg i = 1;i <= n;i++) poi[i].power = Read(1);
for(reg i = 1;i <= m;i++)
{
int u = Read(1),v = Read(1);
addedge(u,v);
}
for(reg i = 1;i <= n;i++)
if(!poi[i].dfn) tarjan(i);
for(reg i = 1;i <= m;i++)
{
int u = edge[i].u,v = edge[i].v;
if(poi[u].scc != poi[v].scc)
{
int sccu = poi[u].scc,sccv = poi[v].scc;
scc[sccv].In++,behind[sccu].push_back(sccv),ahead[sccv].push_back(sccu);
}
}
topo();
for(reg i = 0;i < seq.size();i++)
{
int x = seq[i];
dp[x] = scc[x].power;
for(reg j = 0;j < ahead[x].size();j++)
dp[x] = max(dp[ahead[x][j]] + scc[x].power,dp[x]);
ans = max(ans,dp[x]);
}
printf("%d
",ans);
return 0;
}
(Tarjan)缩点后 求入度为零的点の数
#include <stack>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define reg register int
#define isdigit(x) ('0' <= x&&x <= '9')
template<typename T>
inline T Read(T Type)
{
T x = 0,f = 1;
char a = getchar();
while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
return x * f;
}
const int MAXN = 1e5 + 10,MAXM = 5e5 + 10;
bool vis[MAXN],instack[MAXN];
stack<int> st;
vector<int> seq,ahead[MAXN],behind[MAXN];
int n,cnt,d,cnt_scc,ans,dp[MAXN],head[MAXN],tot;
struct point
{
int scc,dfn,low;
}poi[MAXN];
struct SCC
{
int In;
}scc[MAXN];
struct node
{
int u,v,next;
}edge[MAXM];
inline void addedge(int u,int v)
{
edge[++cnt].v = v;
edge[cnt].u = u;
edge[cnt].next = head[u];
head[u] = cnt;
}
inline void tarjan(int x)
{
poi[x].dfn = poi[x].low = ++tot;
vis[x] = 1,st.push(x),instack[x] = 1;
for(reg i = head[x];i;i = edge[i].next)
{
int v = edge[i].v;
if(!vis[v])
{
tarjan(v);
poi[x].low = min(poi[x].low,poi[v].low);
} else if(instack[v]) poi[x].low = min(poi[x].low,poi[v].dfn);
}
if(poi[x].dfn == poi[x].low)
{
scc[++cnt_scc].In = 0;
while(1)
{
int t = st.top();st.pop();
poi[t].scc = cnt_scc;instack[t] = 0;
if(t == x) break;
}
}
return;
}
int main()
{
n = Read(1);int m = Read(1);
for(reg i = 1;i <= m;i++)
{
int u = Read(1),v = Read(1);
addedge(u,v);
}
for(reg i = 1;i <= n;i++)
if(!poi[i].dfn) tarjan(i);
for(reg i = 1;i <= m;i++)
{
int u = edge[i].u,v = edge[i].v;
if(poi[u].scc != poi[v].scc)
{
int sccu = poi[u].scc,sccv = poi[v].scc;
scc[sccv].In++,behind[sccu].push_back(sccv),ahead[sccv].push_back(sccu);
}
}
for(reg i = 1;i <= cnt_scc;i++)
ans += !scc[i].In;
printf("%d
",ans);
return 0;
}
先判是否有解 (DFS)
再(Tarjan)缩点后,求入读为零的点的权值之和最小(缩点后的点的权值保持前面的点权最小值)
#include <stack>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define reg register int
#define isdigit(x) ('0' <= x&&x <= '9')
template<typename T>
inline T Read(T Type)
{
T x = 0,f = 1;
char a = getchar();
while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
return x * f;
}
const int MAXN = 1e5 + 10,MAXM = 5e5 + 10;
bool vis[MAXN],esp[MAXN],instack[MAXN];
stack<int> st;
int area,n,cnt,cnt_scc,ans,head[MAXN],tot;
struct point {int scc,dfn,low,cost;} poi[MAXN];
struct SCC {int In,cost;} scc[MAXN];
struct node {int u,v,next;} edge[MAXM];
inline void addedge(int u,int v)
{
edge[++cnt].v = v;
edge[cnt].u = u;
edge[cnt].next = head[u];
head[u] = cnt;
}
inline void dfs(int x)
{
vis[x] = 1;
++area;
for(reg i = head[x];i;i = edge[i].next)
{
int v = edge[i].v;
if(!vis[v]) dfs(v);
}
return;
}
inline void tarjan(int x)
{
poi[x].dfn = poi[x].low = ++tot;
vis[x] = 1,st.push(x),instack[x] = 1;
for(reg i = head[x];i;i = edge[i].next)
{
int v = edge[i].v;
if(!vis[v])
{
tarjan(v);
poi[x].low = min(poi[x].low,poi[v].low);
} else if(instack[v]) poi[x].low = min(poi[x].low,poi[v].dfn);
}
if(poi[x].dfn == poi[x].low)
{
++cnt_scc;
while(1)
{
int t = st.top();st.pop();
scc[cnt_scc].cost = min(scc[cnt_scc].cost,poi[t].cost);
poi[t].scc = cnt_scc,instack[t] = 0;
if(t == x) break;
}
}
return;
}
int main()
{
memset(scc,0,sizeof(scc));
n = Read(1);
int p = Read(1),m;
for(reg i = 1;i <= n;i++) scc[i].cost = poi[i].cost = MAXN;
for(reg i = 1;i <= p;i++)
{
int k;
poi[k = Read(1)].cost = Read(1);
esp[k] = 1;
}
m = Read(1);
for(reg i = 1;i <= m;i++)
{
int u = Read(1),v = Read(1);
if(u == v) continue;
addedge(u,v);
}
for(reg i = 1;i <= n;i++) if(esp[i]) dfs(i);
if(area < n)
{
printf("NO
");
for(reg i = 1;i <= n;i++)
if(!vis[i])
{
printf("%d",i);
return 0;
}
}
memset(vis,0,sizeof(vis));
for(reg i = 1;i <= n;i++)
if(!poi[i].dfn) tarjan(i);
for(reg i = 1;i <= m;i++)
{
int u = edge[i].u,v = edge[i].v;
if(poi[u].scc != poi[v].scc)
{
int sccu = poi[u].scc,sccv = poi[v].scc;
scc[sccv].In++;
}
}
for(reg i = 1;i <= cnt_scc;i++)
if(!scc[i].In)
ans += scc[i].cost;
printf("YES
%d
",ans);
return 0;
}
还有一些题