題解:P11215 【MX-J8-T3】水星湖

godmoo發表於2024-10-23

依舊是模擬賽賽題。

Hint

Analysis

首先你注意到兩棵相鄰的樹是一定不會死的,所以可能會死的只有自己種下去的樹,佇列維護。

接著考慮對於每個位置, \(\text{bfs}\) 維護一個最小的長出樹的時間 \(vis[i][j]\),最後暴力統計答案即可。

具體細節看註釋。

Code

#include<bits/stdc++.h>
#define pb push_back
#define is insert
#define fi first
#define se second
#define mkp make_pair
#define mathmod(a,m) (((a)%(m)+(m))%(m))
#define mem(a,b) memset(a,b,sizeof a)
#define cpy(a,b) memcpy(a,b,sizeof b)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
namespace FastIO{
	const int MX=1<<20;
	#ifdef ONLINE_JUDGE
	char buf[MX],*p1,*p2;
	#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,MX,stdin),p1==p2)?0:*p1++)
	#else
	#define gc() getchar()
	#endif
	char pbuf[MX],*p3=pbuf;
	inline void pc(const char c){if(p3-pbuf==MX)fwrite(pbuf,1,MX,stdout),p3=pbuf;*p3++=c;}
	inline void flush(){fwrite(pbuf,1,p3-pbuf,stdout),p3=pbuf;}
	template<class T> inline void read(T& p){
		T x=0,f=1;char c=gc();
		while(c<'0'||c>'9'){if(c=='-')f=0;c=gc();}
		while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=gc();
		p=f?x:-x;
	}
	template<class T,class ... Args> inline void read(T& p,Args&... args){read(p),read(args...);}
	template<class T> inline void write(T x){
		if(!x){pc('0');return;}
		static short sta[40];short tp=0;
		if(x<0) pc('-'),x=-x;
		do{sta[tp++]=x%10,x/=10;}while(x);
		while(tp) pc(sta[--tp]+'0');
	}
	template< > inline void write(const char x){pc(x);}
	template< > inline void write(const char* x){for(int i=0;x[i];i++)pc(x[i]);}
	template< > inline void write(const string x){for(char c:x)pc(c);}
	template<class T,class ... Args> inline void write(T x,Args... args){write(x),write(args...);}
	#undef gc
}
using namespace FastIO;
const int N=3005;
const int INF=0x7f7f7f7f;
int n,m,q,r,k,tim,cnt,vis[N][N];
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
short a[N][N];
struct Query{int t,x,y;};
queue<Query> que;
void bfs(Query s){
	queue<Query> q;
	// 開頭也要判斷,記得更新 vis
	if(vis[s.x][s.y]>s.t) vis[s.x][s.y]=s.t,q.push(s);
	while(!q.empty()){
		auto u=q.front();q.pop();
		for(int i=0;i<4;i++){
			int px=u.x+dir[i][0],py=u.y+dir[i][1],pt=u.t+1;
			if(px<1||px>n||py<1||py>m) continue;
			if(a[px][py]) continue; // 不是湖
			bool flag=0;
			for(int j=0;j<4;j++){ // 判斷周圍是否有水
				// 不能方向與 i 相反,可以看一下 dir[],小最佳化
				if(j==(i^1)) continue;
				int qx=px+dir[j][0],qy=py+dir[j][1];
				if(qx<1||qx>n||qy<1||qy>m) continue;
				if(a[qx][qy]){flag=1;break;}
			}
			if(!flag) continue;
			// 若不是更優的就沒必要繼續往下搜了,記得更新 vis
			if(vis[px][py]>pt) vis[px][py]=pt,q.push((Query){pt,px,py});
		}
	}
}
bool chk(int x,int y){
	for(int j=0;j<4;j++){
		int qx=x+dir[j][0],qy=y+dir[j][1];
		if(qx<1||qx>n||qy<1||qy>m) continue;
		if(a[qx][qy]||vis[qx][qy]<=tim) return true;
	}
	return false;
}
int main(){
	read(n,m,q,r,k);
	for(int i=1,ai1,bi1,ai2,bi2;i<=q;i++){
		read(ai1,bi1,ai2,bi2);
		for(int j=ai1;j<=ai2;j++) for(int k=bi1;k<=bi2;k++) a[j][k]=1;
	}
	mem(vis,0x7f);
	for(int i=1;i<=r;i++){
		Query tmp;read(tmp.t,tmp.x,tmp.y);
		tim=tmp.t;
		// 注意下面是 <tim,因為有可能有連續的相同的 t,所以等到他死透了再 pop 掉 :)
		while(!que.empty()&&que.front().t<tim){
			auto u=que.front();que.pop();
			if(!chk(u.x,u.y)) vis[u.x][u.y]=INF;
		}
		bfs(tmp);
		que.push((Query){tmp.t+k,tmp.x,tmp.y});
	}
	// t<=1e9,所以一個位置最晚在 1e9+3000*3000 個單位時間生長出樹,暴力一點直接開到 2e9,注意 INF 也要開的比 2e9 大
	tim=2e9;
	while(!que.empty()&&que.front().t<tim){
		auto u=que.front();que.pop();
		if(!chk(u.x,u.y)) vis[u.x][u.y]=INF;
	}
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(!a[i][j]&&vis[i][j]<INF) cnt++;
	write(cnt,'\n'),flush();
	return 0;
}

好像還跑得挺快?最優解第二頁?

相關文章