BZOJ 1104 POI2007 洪水pow 并查集

题目大意:给定一张地势图,所有的点都被水淹没,现在有一些关键点,要求放最少的水泵使所有关键点的水都被抽干

前排感谢VFK

首先可以证明一定存在一种最优解使所有的水泵都在关键点上

那么我们将所有关键点按照高度排序,从小到大枚举每个关键点

对于每个关键点x,我们将所有高度小于等于x点的点都加入并查集并将相邻的合并

由于x是并查集中最高的点,因此并查集中任意一个点放置水泵都会导致点x被抽干

故如果x所在并查集中已经放置过水泵,则无需在x点放置水泵

否则就要在x点放置一个水泵

时间复杂度O(mnlog(mn))

一开始写了个O(1000mn)的SPFA结果T到死- -

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 1010
using namespace std;
struct abcd{
	int height,x,y;
	abcd() {}
	abcd(int _,int __,int ___):
		height(_),x(__),y(___) {}
	bool operator < (const abcd &a) const
	{
		return height < a.height;
	}
}cities[M*M],map[M*M];
int n,m,ans,tot;
int a[M][M];
namespace Union_Find_Set{
	pair<int,int> fa[M][M];
	bool pumped[M][M];
	pair<int,int> Find(pair<int,int> x)
	{
		if(fa[x.first][x.second]==make_pair(0,0)||fa[x.first][x.second]==x)
			return fa[x.first][x.second]=x;
		return fa[x.first][x.second]=Find(fa[x.first][x.second]);
	}
	void Union(pair<int,int> x,pair<int,int> y)
	{
		x=Find(x);y=Find(y);
		if(x==y) return ;
		fa[x.first][x.second]=y;
		pumped[y.first][y.second]|=pumped[x.first][x.second];
	}
}
void Insert(int x,int y)
{
	using namespace Union_Find_Set;
	static const int dx[]={1,-1,0,0};
	static const int dy[]={0,0,1,-1};
	int i;
	for(i=0;i<4;i++)
	{
		int xx=x+dx[i],yy=y+dy[i];
		if(xx<=0||yy<=0||xx>m||yy>n)
			continue;
		if(a[xx][yy]>a[x][y])
			continue;
		Union(make_pair(x,y),make_pair(xx,yy));
	}
}
int main()
{
	using namespace Union_Find_Set;
	int i,j;
	cin>>m>>n;
	for(i=1;i<=m;i++)
		for(j=1;j<=n;j++)
		{
			scanf("%d",&a[i][j]);
			if(a[i][j]>0)
				new (&cities[++tot])abcd(a[i][j],i,j);
			else
				a[i][j]=-a[i][j];
			new (&map[i*n-n+j])abcd(a[i][j],i,j);
		}
	sort(cities+1,cities+tot+1);
	sort(map+1,map+m*n+1);
	for(j=1,i=1;i<=tot;i++)
	{
		for(;j<=m*n&&map[j].height<=cities[i].height;j++)
			Insert(map[j].x,map[j].y);
		pair<int,int> temp=Find(make_pair(cities[i].x,cities[i].y));
		if(pumped[temp.first][temp.second])
			continue;
		++ans,pumped[temp.first][temp.second]=true;
	}
	cout<<ans<<endl;
	return 0;
}
时间: 2024-11-29 04:19:57

BZOJ 1104 POI2007 洪水pow 并查集的相关文章

[POI2007]洪水pow 并查集

我们先得出一个结论:水泵要建在城市上.因为如果在非城市上建能把其他一些城市抽干,那么在城市上建也是一个效果(自己画图感性理解一下) 然后我们明白抽水的条件:周围的高度要>=自身的高度,这样会抽完它.如果低的话,会降低旁边位置的水位(这很重要) 然后我们枚举每一个城市,看它用不用建造.这样在每一个城市,枚举所有位置,看这个位置能不能被四周的抽干,这样用并查集维护,能抽干的都是一个祖先 这样枚举完一遍后,看这个城市所连的并查集有没有被抽干,如果没有,就在那里建造即可 #include<iostre

[POI2007]洪水pow 题解

[POI2007]洪水pow 时间限制: 5 Sec  内存限制: 128 MB 题目描述 AKD市处在一个四面环山的谷地里.最近一场大暴雨引发了洪水,AKD市全被水淹没了.Blue Mary,AKD市的市长,召集了他的所有顾问(包括你)参加一个紧急会议.经过细致的商议之后,会议决定,调集若干巨型抽水机,将它们放在某些被水淹的区域,而后抽干洪水.你手头有一张AKD市的地图.这张地图是边长为m*n的矩形,被划分为m*n个1*1的小正方形.对于每个小正方形,地图上已经标注了它的海拔高度以及它是否是A

[POI2007]洪水pow

问题 E: [POI2007]洪水pow 时间限制: 5 Sec  内存限制: 128 MB 题目描述 AKD市处在一个四面环山的谷地里.最近一场大暴雨引发了洪水,AKD市全被水淹没了.Blue Mary,AKD市的市长,召集了他的所有顾问(包括你)参加一个紧急会议.经过细致的商议之后,会议决定,调集若干巨型抽水机,将它们放在某些被水淹的区域,而后抽干洪水.你手头有一张AKD市的地图.这张地图是边长为m*n的矩形,被划分为m*n个1*1的小正方形.对于每个小正方形,地图上已经标注了它的海拔高度以

BZOJ 3562: [SHOI2014]神奇化合物 并查集+dfs

点击打开链接 注意到20w条边,但是询问只有1w,所以有很多边是从头到尾不变的. 首先离线处理,将从未删除的边缩点,缩点后的图的点数不会超过2w,对于每一次add或者delete,直接dfs看是否能从a走到b,然后维护一个ans. 数据不强,不然这种复杂度起码要跑10s.. #include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> using namespace st

bzoj 2733 永无乡 - 并查集 - 线段树

永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输

bzoj 2936 [Poi 1999] 降水 - 并查集

题目传送门 需要root权限的传送门 题目大意 有一个$n\times m$的网格图,每一格都有一个高度.一次降雨过后问最多能积多少水. 考虑算每一高度能储存的水的量. 如果小于等于这个高度的格子和边界连通,那么水就会流走,这一部分不能算入答案. 所以用并查集维护高度小于等于当前高度的格子的连通性.每次答案加已经找到的格子数目减去和边界连通的格子数. Code 1 /** 2 * bzoj 3 * Problem#2936 4 * Accepted 5 * Time: 56ms 6 * Memo

【BZOJ 3674】可持久化并查集加强版&amp;【BZOJ 3673】可持久化并查集 by zky 用可持久化线段树破之

最后还是去掉异或顺手A了3673,,, 并查集其实就是fa数组,我们只需要维护这个fa数组,用可持久化线段树就行啦 1:判断是否属于同一集合,我加了路径压缩. 2:直接把跟的值指向root[k]的值破之. 3:输出判断即可. 难者不会,会者不难,1h前我还在膜这道题,现在吗hhh就当支持下zky学长出的题了. 3673: #include<cstdio> #include<cstring> #include<algorithm> #define read(x) x=ge

BZOJ 1116 [POI2008]CLO(并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1116 [题目大意] Byteotia城市有n个towns,m条双向roads.每条road连接两个不同的towns, 没有重复的road.你要把其中一些road变成单向边使得:每个town都有且只有一个入度 [题解] 我们发现当一个连通块边数大于等于其点数的时候就满足条件, 那么此题如果出现边数少于点数的连通块就不成立, 用并查集能够完成判断. [代码] #include <cstd

BZOJ1104 : [POI2007]洪水pow

从小到大枚举高度i: 对于所有高度为i的点x,将它与它四周相邻的高度不超过i的点所在的集合合并 对于所有高度为i的城市x,如果它所在集合没有放置水泵,则需要放置 并查集维护,时间复杂度$O(nm)$. #include<cstdio> #define N 1010 int n,m,i,j,k,x,y,a[N][N],id[N][N],f[N*N],v[N*N],cnt,dx[4]={-1,1,0,0},dy[4]={0,0,-1,1},ans; struct E{int x,y;E*nxt;}