P1653 [USACO04DEC] Cow Ski Area G

发布时间 2023-09-11 15:38:44作者: One_JuRuo

如果把每个方格看作一个点,就是这道题的子任务 B 了。

思路

首先看到目标是保证任意方格可以互通,就可以想到应该是一道强连通分量的题,只要按照题目的要求建图,就可以得到一个有向图,那么用 tarjan 缩点后,就可以得到一个无环的有向图。

这样一个无向图,对于每个有入度有出度的点,肯定都是按照顺序走,最后是无出度的点停下,所以我们肯定需要连一些从无出度的点出发的边。

那么,对于无入度的点,如果起点不是这个点,就无法到达这个点,所以肯定也需要连一些到达这些点的边。

无出度和无入度可以连在一起,令无出度的点有 \(x\) 个,无入度的点有 \(y\) 个,那么答案至少都是 \(\min(x,y)\)

不过需要注意的是,不是无出度和无入度的点简单两两匹配就好,实际情况需要交叉连边,因为无出度和无入度的点一定有对应关系,那么连边时应该避免连成若干个环。但是这道题只需要得出连边数量,不需要得出如何连边,所以可以不用考虑。

还有需要注意的是,如果缩完点,只剩下一个点,那么就不需要连边。

AC code

#include<bits/stdc++.h>
using namespace std;
int n,m,a[505][505],dfn[250005],low[250005],dcnt,num,id[250005],z[250005],top,in_z[250005],e[1000005],ne[1000005],h[250005],idx=1,in[250005],out[250005],ans1,ans2;
inline void add(int a,int b){e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
inline int get(int i,int j){return (i-1)*m+j;}
inline void ade(int i,int j)
{
	if(i>1)
	{
		if(a[i][j]>=a[i-1][j]) add(get(i,j),get(i-1,j));
		if(a[i][j]<=a[i-1][j]) add(get(i-1,j),get(i,j));
	}
	if(j>1)
	{
		if(a[i][j]>=a[i][j-1]) add(get(i,j),get(i,j-1));
		if(a[i][j]<=a[i][j-1]) add(get(i,j-1),get(i,j));
	}
}
void dfs(int u)
{
	dfn[u]=low[u]=++dcnt,z[++top]=u,in_z[u]=1;
	for(int i=h[u];i;i=ne[i])
		if(!dfn[e[i]]) dfs(e[i]),low[u]=min(low[u],low[e[i]]);
		else if(in_z[e[i]]) low[u]=min(low[u],dfn[e[i]]);
	if(dfn[u]==low[u])
	{
		++num;
		while(z[top+1]!=u) id[z[top]]=num,in_z[z[top--]]=0;
	}
}
int main()
{
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",&a[i][j]),ade(i,j);
	for(int i=1;i<=n*m;++i) if(!dfn[i]) dfs(i);
	for(int i=1;i<=n*m;++i) for(int j=h[i];j;j=ne[j]) if(id[i]!=id[e[j]]) ++in[id[e[j]]],++out[id[i]];
	for(int i=1;i<=num;++i){if(!in[i]) ++ans1;if(!out[i]) ++ans2;}
	printf("%d",(num==1)?0:max(ans1,ans2));
	return 0;
}