【题解】P12019 [NOISG 2025 Finals] 洪水
P12019 [NOISG 2025 Finals] 洪水
题意
给定一个 \(n\times m\) 的网格,每个格子有数字,第 \(i\) 行 \(j\) 列的为 \(a_{i,j}\in \{0,1\}\),\(1\) 表示该格子为建筑格子,\(0\) 表示为空格子。
初始时在空格子 \((x,y)\) 放水,然后会有如下淹没规律:
-
如果一个空格子与至少一个被淹没的格子相邻,它会被淹没。
-
如果一个建筑格子与至少两个被淹没的格子相邻,它会被淹没。
水不会流出边界。
记最终状态时被淹没的格子数为 \(f(x,y)\)。
求出 \(\displaystyle \sum_{i=1}^n \sum_{j=1}^m [a_{i,j}=0]f(i,j)\)。
\(n,m\le 5000\)。
题解
知识点:扫描线,线段树,单调栈,堆。
启发:
- 特定情况下使用堆实现延迟删除。
感觉全是基本功啊,可为什么前前后后搞了了这么久呢。
一开始没用堆,用的 multiset 卡常卡了一天还没卡过去,很难受。
思考 0
-
空格子不会被淹没,当且仅当四联通域没有被淹没的。
-
建筑格子不会被淹没,当且仅当四联通域至多有一个被淹没的。
被淹没的格子外围,要么是边界,要么是建筑格子。
思考 1
想一想被淹没的格子组成的形状。
当淹没的格子外围有凸出时,凸出左右边包住的不会是边界,因为边界是平的,所以只能是建筑格子,而这也是不可能的,建筑格子显然也会被淹没,所以不存在凸出,同理,也不存在凹进。所以外围是平的。
可以得出重要结论:被淹没的所有格子组成的图形一定是矩形。而矩形外围一圈(不包括四个角斜向的)不是建筑格子就是边界。
考虑将边界全变为建筑格子,可以发现他们一定不会被淹没,所以可以等效。
那么矩形的上下左右边界就都是建筑格子。
思考 2
那么一个空格子的贡献就是包含它的最小矩形的面积。
考虑处理出所有矩形,在这之前,先思考其数量级,由于矩形内部一定不能存在一整行/列的建筑格子(不然就会变成两个矩形),一个矩形的任意两对边至多同时贡献到一个矩形,这个限制比较强,故其数量级大概是 \(O(nm)\) 的。
处理出两个数组 \(ha_{i,j},la_{i,j}\),分别表示如果 \((i,j)\) 是建筑格子,其向行/列增加方向最长的连续建筑格子的长度(包括自己)。
处理出所有矩形,可以先 \(O(m)\) 枚举列 \(y\) 为其左边界,然后在每一列里面枚举上下边界的 \(x\) 坐标,判定能否组成矩形,如果能就加入,判定这一步会排除矩形内一整列建筑格子的情况。
当然,这里并不能 \(O(n^2)\) 枚举,考虑两个 \(i,j(i<j)\),如果 \(la_{i,y}\le la_{j,y}\),则 \(i\) 肯定无法和后面的搭配形成矩形了,倘若组成矩形,那么第 \(j\) 行会其中形成一个一整行的建筑格子,所以肯定不合法。
这个过程可以用单调栈实现,时间复杂度 \(O(n)\),这一步会排除矩形内一整行建筑格子的情况。
思考 3
根据之前得出的,一个空格子的贡献就是包含它的最小矩形的面积,考虑扫描线,对哪一维扫都可以,动态加入/删除矩形,权值是该矩形的面积。
一下讨论的是对 \(y\) 坐标的扫描。
扫描线达到了一个消维的效果,那么问题就转变为了:
- 维护一个长度为 \(n\) 的序列 \(a\),初始时每个位置都为 \(+\infty\),每次给出三元组 \((l,r,s)\),对每个 \(i\in [l,r]\) 执行 \(a_i\leftarrow \min(a_i,s)\),同时还需要支持随机撤销之前三元组 \((l,r,x)\) 的作用效果,单点询问 \(a_i\) 当前的值。
第一眼有一个线段树的做法,每个节点维护一个 multiset 的做法。
区间赋值时,递归到完全被操作区间的包含的节点区间时将 \(s\) 加入该节点的 multiset。
查询时,顺着查询位置走线段树上的节点直到走到叶子,答案就是路径上 multiset 里的最小值。
只可惜这个做法常数太大了,卡常卡了一页还没过,于是有一个常数更小更聪明的堆做法。
每个节点开一个 pair<int,int> 的 priority_queue,加入节点的形式是一个 pair,面积 \(s\) 为 first,该矩形作用时间右端点为 second,查询时先不断弹出 second 小于当前扫描到的列的队头,然后取此时的队头。
此时优先队列里面可能还有超时的元素,为什么不弹呢?其实这里用到了延迟删除的思想,不弹它并不会影响答案,那就等到队头是他的时候再弹,因为时间是单调的,某个时刻超时了,那么后面所有时刻都是超时状态。
总时间复杂度 \(O(nm \log^2 n)\),虽然和 multiset 做法的复杂度相同,但是常数小了不少。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define N 5005
#define ll long long
int n,m,ha[N][N],la[N][N];
int st[N],tp,a[N][N];
struct rec{
int l,r,s,ed;
};
vector<rec>ad[N];
inline void chk(int l,int r,int j){
if(r-l-1<=0||ha[l+1][j-1]<r-l-1){
return;
}
int i=j-1,ed=j+min(la[l][j],la[r][j])-1;
while(ha[l+1][i+1]<r-l-1&&i<=ed){
i++;
}
if(i>ed||i<j){
return;
}
// cout<<"suc\n";
ad[j].pb({l+1,r-1,(r-l-1)*(i-j+1),i});
// cout<<l+1<<' '<<r-1<<' '<<j<<' '<<i<<' '<<"\n";
}
struct segt{
#define mid ((l+r)>>1)
priority_queue<pr,vector<pr>,greater<pr>>q[N<<2];
inline void add(int L,int R,int k,int l,int r,int d,int ed){
if(L<=l&&R>=r){
q[k].push({d,ed});
return;
}
if(L<=mid){
add(L,R,k*2,l,mid,d,ed);
}
if(R>mid){
add(L,R,k*2+1,mid+1,r,d,ed);
}
}
inline int ask(int L,int k,int l,int r,int ed){
int ans=1e9;
while(1){
while(sz(q[k])&&q[k].top().se<ed){
q[k].pop();
}
if(sz(q[k])){
ans=min(ans,q[k].top().fi);
}
if(l==r){
break;
}
if(L<=mid){
r=mid;
k=k*2;
}
else{
l=mid+1;
k=k*2+1;
}
}
return ans;
}
#undef mid
}t;
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
// ios::sync_with_stdio(0);
// cin.tie(0);cout.tie(0);
cin>>n>>m;
n+=2,m+=2;
rep(i,1,n){
a[i][1]=a[i][m]=1;
}
rep(i,1,m){
a[1][i]=a[n][i]=1;
}
#define getchar getchar_unlocked
char c=0;
rep(i,2,n-1){
rep(j,2,m-1){
c=getchar();
while(c!='0'&&c!='1'){
c=getchar();
}
a[i][j]=c-'0';
}
}
per(i,1,n){
per(j,1,m){
if(a[i][j]){
ha[i][j]=ha[i+1][j]+1;
la[i][j]=la[i][j+1]+1;
}
}
}
per(j,2,m-1){
tp=0;
rep(i,1,n){
while(tp){
chk(st[tp],i,j);
if(la[st[tp]][j]>la[i][j]){
break;
}
tp--;
}
st[++tp]=i;
}
}
ll ans=0;
rep(j,1,m){
for(rec u:ad[j]){
t.add(u.l,u.r,1,1,n,u.s,u.ed);
// cout<<j<<' '<<u.l<<' '<<u.r<<" add\n";
}
rep(i,1,n){
if(a[i][j]){
continue;
}
ans+=t.ask(i,1,1,n,j);
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号