CF1534F1题解

首先,对于一个在第 \(i\)\(j\) 列的沙子,如果他开始下降,他能够使哪些沙子下降呢?

很容易得知是第 \(j-1,j,j+1\) 列所有行号不小于 \(i\) 的沙子。

对于沙子 \(u\) 下降能够使沙子 \(v\) 下降,我们连一条边 \((i,j)\)。然后缩点,对于度数为 \(0\) 的点,我们他不可能因为别的沙子下降而下降,所以我们得手动扰动这些沙子。

那么答案就是缩点后的图中度数为 \(0\) 的点的个数。

然而暴力连边的时空复杂度是 \(O(n^2m)\) 的,我们考虑省掉一些不要的边。

对于三个点 \((u,v,w)\),若存在边 \((u,v)\)\((v,w)\),那么边 \((u,w)\) 明显可以被省掉。

根据这个优化,我们可以省掉很多边。

比如对于三个点 \(u,v,w\),其中 \(v\)\(u\) 的左下方,\(w\)\(v\) 正下方,那么 \(u\) 很明显只需要连接 \(v\) 而不需要连接 \(w\)

所以,对于一个点,需要连接这些边:

  1. \((i,j)\) 有一块沙子且 正上方一格 也有一块沙子,连接 \((i,j)\)\((i-1,j)\)
  2. \((i,j)\) 的正下方存在沙子,连接正下方第一块沙子;
  3. \((i,j)\) 的下一列的下方存在沙子,连接下一列的下方第一块沙子;
  4. \((i,j)\) 的上一列的下方存在沙子,连接上一列的下方第一块沙子;

对于 \(3\) 类边和 \(4\) 类边,反过来从左上/右上第一个沙子连接自己,预处理正上方第一块沙子即可。

当然也可以预处理正下方的第一块沙子,然后不反过来。

code:

#include<cstdio>
#include<vector>
const int M=4e5+5;
int n,m,ans,dfc,BCC,in[M],on[M],bl[M],dfn[M],low[M];std::vector<int>G[M];
bool val[M],istk[M];int top,stk[M];
inline int id(const int&x,const int&y){
	return x?(x-1)*m+y:0;
}
inline int min(const int&a,const int&b){
	return a>b?b:a;
}
inline char read_c(){
	char s;while(s=getchar(),s!='.'&&s!='#');
	return s;
}
void Tarjan(int u){
	istk[stk[++top]=u]=true;
	low[u]=dfn[u]=++dfc;
	for(int&v:G[u]){
		if(!dfn[v]){
			Tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(istk[v])low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){
		register int v;++BCC;
		do bl[v=stk[top--]]=BCC,istk[v]=false;while(v!=u);
	}
}
signed main(){
	register int i,j,lst;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i){
		for(j=1;j<=m;++j){
			on[id(i,j)]=(val[id(i,j)]=read_c()=='#')?id(i,j):on[id(i-1,j)];
		}
	}
	for(j=1;j<=m;++j){
		lst=0;
		for(i=n;i>=1;--i){
			if(!val[id(i,j)])continue;
			if(lst)G[id(i,j)].push_back(lst);lst=id(i,j);
			if(val[id(i-1,j)])G[id(i,j)].push_back(id(i-1,j));
			if(j!=1&&on[id(i,j-1)])G[on[id(i,j-1)]].push_back(id(i,j));
			if(j!=m&&on[id(i,j+1)])G[on[id(i,j+1)]].push_back(id(i,j));
		}
	}
	for(i=1;i<=n*m;++i)if(!dfn[i]&&val[i])Tarjan(i);
	for(i=1;i<=n*m;++i){
		for(int&v:G[i])if(bl[v]!=bl[i])++in[bl[v]];
	}
	for(i=1;i<=BCC;++i)ans+=!in[i];
	printf("%d\n",ans);
}
posted @ 2022-01-10 16:38  Prean  阅读(25)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};