[CF1534F]Falling Sand
作者:互联网
题目
思路
显然可以转化为图论。能够激发就连有向边。也很容易联想到,是不是某个方块被激发,能够满足条件的列是一个区间呢?
事实上,上面这个东西只需要一个保证:每个方块被激发时,自己这一列就被满足了。为什么这样就可以了呢?可以归纳法。如果激发的是一个区间,并且自己这一列可以得到满足,那么一个方块的激发范围是其所有后继的区间的并集,再加上自己这一列。由于其后继一定是相邻的两列(或者自己这一列),通过自己这一列就能连接起来。否则,就很可能连接不起来。
转念一想,显然这是错误的。某一列的最靠下的一个方块被激发了,这一列很有可能没有被满足嘛!所以打消了这个念头,试着去进行图论转化。无果。
回到这个做法上。需要发现的是,某些列是不需要单独考虑的,它们在别的列满足条件的时候,自动也会满足条件。不妨去掉这些列。而上面的例外情况——某个方块低于这一列的 “满足条件” 方块,它能覆盖一个区间,此区间不包含自己——覆盖的列都是这样的无用列。因为这一列的 “满足条件” 方块也能激发它们。所以上面的危机解决了!
总结一下就是:不构成区间的列是无关紧要的,将其去除,剩下的每个点都是一个区间覆盖。然后就变成经典问题了。时间复杂度 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