[ZOJ 3354] DoIt is Being Flooded [最短路+并查集]

本文介绍了一种在洪水逐年升高的背景下,计算不同年份岛屿经济效益的方法。利用Dijkstra算法计算各位置被淹时间,结合并查集更新岛屿面积变化,最终通过公式计算经济效益。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在n*m的棋盘上有一个岛,岛上每个位置都有一个高度,海平面在第0年高度是0,以后逐年增高1单位。如果海平面大于等于该位置的海拔高度,则该位置会被淹没而不再是陆地。注意和海平面相等也是算作会被淹没的。海水只能顺着流过去,如有一块海拔为3的陆地被一圈海拔为4的陆地围着,在海平面为3的时候,中间的海拔为3的陆地是不会被淹没的。海水只能往上下左右四个相邻的格子流,不能走向只有一个公共顶点的斜着的格子。

现有一公式,可计算某年岛屿的经济效益。公式为∑2^Bits(S&a),其中S为每个岛屿的面积,a为一个参数,由输入数据给出,Bits(x)为x在二进制下1的个数。

题目给出岛屿的信息,岛屿规模不超过500*500,每个位置的高度不超过10^6,有10^4次询问,每次给出询问年份d,和参数a,其中d不超过10^6,a不超过10^9,输出每次询问对应的经济效益。

首先计算每个格子实际被海水淹没的时间。实际被淹没的时间为上下左右四个格子被淹没的时间的最小值,和自己的高度的最大值。最外层一圈被淹没的时间为自己的高度。于是整个可以用dijkstra处理。复杂度(500*500*log(500*500))

然后把所有询问离线,排序,倒序处理。这样就把问题从一整个岛屿逐渐沉没分成很多岛屿转化成了每个位置逐渐有陆地浮出,多个岛屿融合成一个岛屿。可以用并查集来做。同时用一个map来记录,面积为S的岛屿有多少个。面积不同的岛屿最多也就不过700个,因为(1+750)*750/2=281625>250000。复杂度计算:每个位置只会浮现一次,在map中处理复杂度为log,并查集中处理复杂度为O(1),所以总复杂度为(500*500*log(500*500))

针对每次询问,我们已经知道了当前的面积不同的岛屿的个数。直接计算即可。提前预处理出每个数在二进制下1的个数。因为使用的是S&a,所以参数不会超过S,即500*500。预处理复杂度为(500*500*log(500*500)),所有查询加起来复杂度为10000*700。

这样每个步骤的复杂度都在10^6,一共10组case,刚好满足要求。

#include <cstdio>
#include <queue>
#include <algorithm>
#include <map>

using namespace std;

inline int in() {
	char c=getchar();
	while (c<'0'||c>'9') c=getchar();
	int ans=0;
	while (c>='0'&&c<='9') {
		ans=ans*10+c-'0';
		c=getchar();
	}
	return ans;
}

const int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int n,m,p,q;
int a[500][500];

inline bool inBoard(int i,int j) {
	return i>=0&&j>=0&&i<n&&j<m;
}

struct QueNode {
	int i,j,v;
	QueNode() {}
	QueNode(int ii,int jj,int vv) {
		i=ii;j=jj;v=vv;
	}
	friend bool operator < (const QueNode &a,const QueNode &b) {
		return a.v>b.v;
	}
};

int b[500][500];
QueNode d[250000];

void calDis() {
	static priority_queue <QueNode> c;
	int i,j,v;
	for (i=0;i<n;i++)
		for (j=0;j<m;j++)
			b[i][j]=-1;
	while (!c.empty()) c.pop();
	for (i=0;i<n;i++) {
		b[i][0]=a[i][0];
		if (m!=1) b[i][m-1]=a[i][m-1];
		c.push(QueNode(i,0,a[i][0]));
		if (m!=1) c.push(QueNode(i,m-1,a[i][m-1]));
	}
	for (j=1;j<m-1;j++) {
		b[0][j]=a[0][j];
		if (n!=1) b[n-1][j]=a[n-1][j];
		c.push(QueNode(0,j,a[0][j]));
		if (n!=1) c.push(QueNode(n-1,j,a[n-1][j]));
	}
	p=0;
	while (!c.empty()) {
		QueNode x=c.top();
		i=x.i; j=x.j; v=x.v;
		c.pop();
		if (v==b[i][j]) {
			d[p++]=x;
			for (int k=0;k<4;k++) {
				int ii=i+dir[k][0],jj=j+dir[k][1];
				if (inBoard(ii,jj)) {
					int vv=max(a[ii][jj],v);
					if (b[ii][jj]==-1||b[ii][jj]>vv) {
						b[ii][jj]=vv;
						c.push(QueNode(ii,jj,vv));
					}
				}
			}
		}
	}
}

struct DisjoinSet {
	int a[250000];
	int n[250000];
	void clear(int nn) {
		for (int i=0;i<nn;i++) {
			a[i]=i;n[i]=1;
		}
	}
	int getSize(int i) {
		i=get(i);
		return n[i];
	}
	int get(int i) {
		if (a[i]==i) return i;
		return a[i]=get(a[i]);
	}
	void tosame(int x,int y) {
		x=get(x);
		y=get(y);
		if (x!=y) n[y]+=n[x];
		a[x]=y;
	}
};

DisjoinSet e;
map<int,int> f;

struct Query{
	int d,a,i;
	friend bool operator < (const Query &a,const Query &b) {
		return a.d>b.d;
	}
};

Query g[10000];
long long ans[10000];
bool upped[250000];

inline void disappear(int x) {
	if (f[x]==1) f.erase(x);
	else f[x]--;
}

void upland(int i,int j) {
	//printf("upland %d %d\n",i,j);
	int tmp=i*m+j,tmp2;
	upped[tmp]=true;
	f[1]++;
	for (int k=0;k<4;k++) {
		int ii=i+dir[k][0],jj=j+dir[k][1];
		if (inBoard(ii,jj)) {
			int tmp2=ii*m+jj;
			if (upped[tmp2]&&e.get(tmp)!=e.get(tmp2)) {
				disappear(e.getSize(tmp));
				disappear(e.getSize(tmp2));
				e.tosame(tmp,tmp2);
				f[e.getSize(tmp)]++;
			}
		}
	}
}

int bitn[250001];
inline int calBitn(int x) {
	int ans=0;
	while (x) {
		ans+=x&1;
		x>>=1;
	}
	return ans;
}
long long cal(int a) {
	long long ans=0;
	for (map<int,int>::iterator it=f.begin();it!=f.end();it++)
		ans+=it->second*(1ll<<bitn[it->first&a]);
	return ans;
}

int main() {
	int i,j;
	for (i=0;i<=250000;i++) bitn[i]=calBitn(i);
	while (scanf("%d%d",&n,&m)!=EOF) {
		for (i=0;i<n;i++)
			for (j=0;j<m;j++)
				a[i][j]=in();
		calDis();
		for (i=0;i<p;i++) upped[i]=false;
		e.clear(p);
		f.clear();
		scanf("%d",&q);
		for (i=0;i<q;i++) {
			scanf("%d%d",&g[i].d,&g[i].a);
			g[i].i=i;
		}
		sort(g,g+q);
		p--;
		for (i=0;i<q;i++) {
			while (p>=0&&d[p].v>g[i].d) {
				upland(d[p].i,d[p].j);
				p--;
			}
			ans[g[i].i]=cal(g[i].a);
			//printf("--year %d\n",g[i].d);
			//for (map<int,int>::iterator it=f.begin();it!=f.end();it++)
				//printf(" %d %d\n",it->first,it->second);
		}
		for (i=0;i<q;i++) {
			printf("%lld\n",ans[i]);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值