NOIP 模拟赛 day5 T2 水 故事题解

题目描述

有一块矩形土地被划分成 \(\small n×m\) 个正方形小块。这些小块高低不平,每一小块都有自己的高度。水流可以由任意一块地流向周围四个方向的四块地中,但是不能直接流入对角相连的小块中。

一场大雨后,由于地势高低不同,许多地方都积存了不少降水。假如你已经知道这块土地的详细信息,你能求出每个小块的积水高度吗?

注意:假设矩形地外围的高度为 \(\small 0\) 。

\(\small1\leq n,m\leq 300 ,-10^9\leq h\leq 10^9\)

solution

实际上最一开始思路本来是从围墙开始向里遍历,用队列维护。

后来发现随便一个数据就能卡掉,于是就换成了小根堆维护。

然后又造了一个数据卡掉,加了一个小细节,加点的时候和原来的点权值取max,然后就可以过了。。

于是我尝试去口胡这道题的题解:

故事开始了

这是一个属于水的世界。

你叫水王,是整个世界的主:海王的得力部下。

有这么一天,你犯下了弥天大错,海王为了惩罚你,将你贬为水鬼,取消了你天水撒花的能力,并把你发配到了一片高低不平的土地上。

海王要知道这里下了水后,每一块地方落了多高的水,并让你从外面的水平面上开始计算。

幸好,他没有夺走你召唤单位量的水和在水上行走的能力,你开始了。

哦,你还有个毛病,你虽然不像水,你能上能下,但你只想往下走,不行的话再走平路,再不行才会向上走,并且你不会在一块地方上走两遍。

你在外面绕了一圈,会找到两大类地方,不比水平面低的,暂时不动;比水平面低的,马上开始走,并召唤相应的单位量水填平(就当它真的这么高了),顺便记录下来,然后继续向里面找,直到周围全都比你高了,就继续回到外面找。

这样一圈下来,你可能走成这样了。(黑色是走过的)

NOIP 模拟赛 day5 T2 水 故事题解

所以说就相当于沿黑色块内部又是一圈,你就会找到这一圈里面最矮的地方爬上去,绕着继续重复找。

最后你能保证所有点都走过一遍了,但怎么保证答案都是对的呢。

你陷入了沉思。。

因为搜到的前面的点已经填过水了,所以里面的只是找到这个点而已,填到多高,之前就已经知道了。

于是你在大约 \(\small O(nm\log{(nm)})\) 左右的时间搞定了

海王特别高兴,又把你恢复成水王了。

故事结束了

( 故事讲得不好,细节体现不全/kk )

看看代码吧(码风清奇,绝对好理解)

#include<bits/stdc++.h>
#define reg register
using namespace std;
typedef long long ll;
const int xx[]={0,0,1,-1};
const int yy[]={1,-1,0,0};
const int N=3e2+10;
int n,m;
ll a[N][N],ans[N][N];
bool vis[N][N];
struct mdzz{
	int x,y;ll val;
	bool operator < (const mdzz &b) const {
		return val>b.val;
	}
};
priority_queue<mdzz> q;
inline ll imax(ll a,ll b){
	return a>b?a:b;
}
inline ll read(){
	ll s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w;
}
inline void BFS(){
	while(!q.empty()){
		mdzz u=q.top(),v;q.pop();
		for(int i=0;i<4;++i){
			v.x=u.x+xx[i];
			v.y=u.y+yy[i];
			if(v.x<1||v.x>n||v.y<1||v.y>m||vis[v.x][v.y])continue;
			v.val=a[v.x][v.y];
			vis[v.x][v.y]=1;
			if(v.val<u.val){
				ans[v.x][v.y]=u.val-v.val;
			}
			q.push({v.x,v.y,imax(u.val,v.val)});
		}
	}
}
int main(){
	//freopen("water.in","r",stdin);
	//freopen("water.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			a[i][j]=read();
		}
		q.push({0,i,0ll});
		q.push({n+1,i,0ll});
	}
	for(int j=1;j<=m;++j){
		q.push({j,0,0ll});
		q.push({j,m+1,0ll});
	}
	BFS();
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			printf("%lld ",ans[i][j]);
		}
		printf("\n");
	}
	return 0;
}

(难得考场过题呀)

上一篇:<2021SC@SDUSC> 开源游戏引擎 Overload 代码模块分析 之 OvGame(五)—— Debug(下)FrameInfo & GameProfiler


下一篇:【无标题】