其他分享
首页 > 其他分享> > [CF1534F]Falling Sand

[CF1534F]Falling Sand

作者:互联网

题目

传送门 to CF

思路

显然可以转化为图论。能够激发就连有向边。也很容易联想到,是不是某个方块被激发,能够满足条件的列是一个区间呢?

事实上,上面这个东西只需要一个保证:每个方块被激发时,自己这一列就被满足了。为什么这样就可以了呢?可以归纳法。如果激发的是一个区间,并且自己这一列可以得到满足,那么一个方块的激发范围是其所有后继的区间的并集,再加上自己这一列。由于其后继一定是相邻的两列(或者自己这一列),通过自己这一列就能连接起来。否则,就很可能连接不起来。

转念一想,显然这是错误的。某一列的最靠下的一个方块被激发了,这一列很有可能没有被满足嘛!所以打消了这个念头,试着去进行图论转化。无果。

回到这个做法上。需要发现的是,某些列是不需要单独考虑的,它们在别的列满足条件的时候,自动也会满足条件。不妨去掉这些列。而上面的例外情况——某个方块低于这一列的 “满足条件” 方块,它能覆盖一个区间,此区间不包含自己——覆盖的列都是这样的无用列。因为这一列的 “满足条件” 方块也能激发它们。所以上面的危机解决了!

总结一下就是:不构成区间的列是无关紧要的,将其去除,剩下的每个点都是一个区间覆盖。然后就变成经典问题了。时间复杂度 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn) 。

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
typedef long long int_;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 400005;
const int infty = (1<<30)-1;

int bel[MaxN], dfsClock;
vector<int> G[MaxN], sta;
int dfn[MaxN], low[MaxN];
bool inSta[MaxN];
void tarjan(int x){
	dfn[x] = low[x] = ++ dfsClock;
	inSta[x] = 1; sta.push_back(x);
	for(int y : G[x])
		if(dfn[y] == 0){
			tarjan(y); // recurse
			low[x] = min(low[x],low[y]);
		}
		else if(inSta[y])
			low[x] = min(low[x],dfn[y]);
	if(dfn[x] == low[x]){
		while(sta.back() != x){
			bel[sta.back()] = x;
			inSta[sta.back()] = 0;
			sta.pop_back();
		}
		bel[x] = x, inSta[x] = 0;
		sta.pop_back();
	}
}

vector<int> tmp[MaxN];
void rebuild(int n){
	for(int i=1; i<=n; ++i)
		if(!dfn[i]) tarjan(i);
	rep(i,1,n) for(int j : G[i])
		if(bel[i] != bel[j])
			tmp[bel[i]].push_back(bel[j]);
	rep(i,1,n) G[i].swap(tmp[i]);
}

bool tag[MaxN]; // useless
void putTag(int x){
	if(tag[x]) return ;
	tag[x] = true;
	for(int y : G[x])
		putTag(y);
}

struct Range{
	int l, r; Range(){}
	Range(int L,int R):l(L),r(R){}
	bool operator < (const Range &t) const {
		return l < t.l;
	}
};
Range operator & (const Range &a,const Range &b){
	return Range(min(a.l,b.l),max(a.r,b.r));
}
Range r[MaxN];
void getRange(int x,int m){
	if(inSta[x]) return ;
	inSta[x] = true;
	for(int y : G[x]){
		getRange(y,m);
		r[x] = r[x] & r[y];
	}
}

char **maze; int col[MaxN];
Range all[MaxN];
int main(){
	int n = readint(), m = readint();
	maze = new char*[n];
	rep(i,0,n-1){
		maze[i] = new char[m+5];
		scanf("%s",maze[i]+1);
		rep(j,1,m){
			if(maze[i][j] != '#')
				continue;
			rep(d,j-1,j+1) if(col[d])
				G[col[d]].push_back(i*m+j);
			if(j && maze[i][j-1] == '#')
				G[i*m+j].push_back(i*m+j-1);
			if(i && maze[i-1][j] == '#')
				G[i*m+j].push_back(i*m+j-m);
			col[j] = i*m+j;
		}
	}
	rebuild(n*m); tag[0] = true;
	rep(j,1,m){
		int a = readint();
		if(a == 0){
			col[j] = 0;
			continue;
		}
		drep(i,n-1,0){
			if(maze[i][j] != '#')
				continue;
			if(-- a) continue;
			col[j] = bel[i*m+j];
			for(int y : G[col[j]])
				putTag(y);
			break; // find key
		}
	}
	rep(i,1,n*m) r[i] = Range(infty,-infty);
	rep(j,1,m) if(!tag[col[j]])
		r[col[j]] = r[col[j]] & Range(j,j);
	rep(i,1,n*m) if(!inSta[i])
		getRange(i,m); // get all
	sort(r+1,r+(n*m)+1);
	int lst = 0, nxt = 0, ans = 0;
	for(int i=1,j=1; i<=m; ++i){
		for(; j<=n*m&&r[j].l==i; ++j)
			nxt = max(nxt,r[j].r);
		if(tag[col[i]]) // deleted
			lst = max(lst,i);
		if(i == lst+1)
			lst = nxt, ++ ans;
	}
	printf("%d\n",ans);
	return 0;
}

标签:back,int,Range,CF1534F,Sand,MaxN,low,Falling,col
来源: https://blog.csdn.net/qq_42101694/article/details/118095145