缩点

老师讲图论-缩点-复习

我想 我没学过 缩点啊 \(OTL\)

先讲 割点

定义 在无向连通图中,如果将其中一个点以及所有连接该点的边去掉,图就不再连通,那么这个点就叫做割点

P3388 【模板】割点(割顶)

主要思想

观察\(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\n",size);
	for(reg i = 1;i <= n;i++)
		if(out[i]) printf("%d ",i);
	return 0;
}

缩点

就将强连通分量的点 缩成一个点

P3387 【模板】缩点

补充:\(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\n",ans);
	return 0;
}

P2002 消息扩散

\(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\n",ans);
	return 0;
}

P1262 间谍网络

先判是否有解 \(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\n");
		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\n%d\n",ans);
	return 0;
}

还有一些题

P1407 [国家集训队]稳定婚姻

P1455 搭配购买

P2169 正则表达式

P2194 HXY烧情侣

P2515 [HAOI2010]软件安装

posted @ 2019-11-02 09:49  resftlmuttmotw  阅读(204)  评论(0编辑  收藏  举报