3485: [Baltic2012]peaks
Time Limit: 40 Sec Memory Limit: 64 MBSubmit: 32 Solved: 18
[ Submit][ Status][ Discuss]
Description
给定一个n*m的地图,每个格子都有一个高度,两个格子相邻当且仅当他们有公共点,每次可以从一个格子上可以走到另一个和他相邻的格子上。我们把一块高度相同并且八连通的格子的集合称之为一个平原,同时把相邻的格子中没有比他更高的点的平原成为峰。现在要你统计出所有的峰的高度和评估值,按高度为第一关键字,评估值为第二关键字从大到小输出。一个峰的评估值是指,如果从这个峰到一个比他高的峰去,所要经过的高度最小的平原的最大值。
Input
第一行给出,N,M。(1≤N,M≤2000, N×M≤10^5
接下来N行M列,描述这个矩阵,其中的数在[1,10^6]之间
Output
如题
Sample Input
21 16 9 11 6 7
21 21 10 14 15 9
18 20 8 9 13 14
11 10 9 9 8 13
8 12 12 14 13 8
7 13 12 9 5 1
Sample Output
21 0
15 11
14 13
13 12
HINT
Source
刚开始题意没看懂,wa了次,后来发现,一片连在一起的高度一样的格子算一块(斜着连也算)。
我们先处理出哪些点是峰,我们只要把每一块都dfs一遍,并且这一块旁边所有点的最大值小于等于这个块的值,那么说明这个块就是峰,选这个块中的一个点打上标记。
然后我们把格子按照格子中值的大小从大到小排序,然后一个个加格子:
每加入一个格子,我们把这个格子和周围的已经出现的格子连边,连边就相当于把两个并查集合并。我们在处理的时候特殊处理一下并查集的根,若是并查集中有打过标记的格子,那么把这个格子当做根,这样的话当两个都打过标记的根相遇,就可以进行更新了。
两个都打过标记的根相遇,如果不是一样大, 我们更新较小的那个根的答案,答案就是当前加入格子的权值,然后把这个根较小的接到根较大的并查集下面。
若是一样大,我们给这两个根连边,然后把一个加入另一个中,在全部更新完后,再把当时没更新的更新掉,两个能合并并且大小一样,那么答案肯定也是一样的,直接赋值就好。
最后把所有的峰都整理出来,排个序输出就可以。
如果看不懂的可以参照代码理解(代码挺丑的,因为刚开始题意理解错了,有点乱)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 2005
#define M 100505
using namespace std;
const int dx[8]={0,0,1,-1,1,1,-1,-1},dy[8]={1,-1,1,-1,-1,0,1,0};
int k,fir[M],n,m,a[N][N],d[M],fa[M],flag[M],cnt,cnt1,ans[M],vis[N][N],vis1[N][N];
struct ha{
int r,nx;
}A[M];
struct he{
int x,y,d,n;
}c[M];
struct he1{
int ans,d;
}C[M];
queue<int> Q;
int gf(int x){
if(fa[x]==x) return x;return fa[x]=gf(fa[x]);
}
bool check(int u){
for(int i=0;i<8;i++){
int x1=c[u].x+dx[i];
int y1=c[u].y+dy[i];
if(a[x1][y1]>a[c[u].x][c[u].y]) return 0;
if(flag[(x1-1)*m+y1]==1) return 0;
}
return 1;
}
void add(int l,int r){
k++;A[k].r=r;A[k].nx=fir[l];fir[l]=k;
}
void modify(int u,int v,int w){
int u1=gf(u),v1=gf(v);
if(u1==v1) return ;
if(flag[u1]&&flag[v1]){
if(d[u1]>d[v1]){
if(ans[v1]==0) ans[v1]=w;
fa[v1]=u1;
}else if( d[u1]<d[v1]){
if(ans[u1]==0) ans[u1]=w;
fa[u1]=v1;
}else{
add(v1,u1);
fa[u1]=v1;
}
}else if(flag[u1]){
fa[v1]=u1;
}else {
fa[u1]=v1;
}
}
bool cmp(he a ,he b){
return a.d>b.d;
}
bool cmp1(he1 a,he1 b){
if(a.d==b.d) return a.ans>b.ans;
return a.d>b.d;
}
void find(int x,int y,int &Mx){
vis1[x][y]=1;
for(int i=0;i<8;i++){
int x1=x+dx[i];
int y1=y+dy[i];
Mx=max(Mx,a[x1][y1]);
if(vis1[x1][y1]) continue;
if(a[x1][y1]==a[x][y])find(x1,y1,Mx);
}
}
int _read(){
int sum=0;char ch=getchar();
while(!(ch>='0'&&ch<='9'))ch=getchar();
while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=getchar();
return sum;
}
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
memset(fir,-1,sizeof(fir));
n=_read();m=_read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
a[i][j]=_read();
cnt++;
fa[cnt]=cnt;
c[cnt].x=i;c[cnt].y=j;c[cnt].d=a[i][j];c[cnt].n=(i-1)*m+j;d[c[cnt].n]=a[i][j];
}
sort(c+1,c+cnt+1,cmp);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!vis1[i][j]) {
int mx=-1;
find(i,j,mx);
if(mx<=a[i][j]) flag[(i-1)*m+j]=1;
}
for(int i=1;i<=cnt;i++){
vis[c[i].x][c[i].y]=1;
for(int j=0;j<8;j++){
int x1=c[i].x+dx[j],y1=c[i].y+dy[j];
if(1<=x1&&x1<=n&&1<=y1&&y1<=m&&vis[x1][y1])
modify(c[i].n,(x1-1)*m+y1,c[i].d);
}
}
for(int i=1;i<=cnt;i++)
if(ans[i]!=0){
Q.push(i);
}
while(!Q.empty()){
cnt1++;
int u=Q.front();Q.pop();
C[cnt1].d=d[u];C[cnt1].ans=ans[u];
for(int i=fir[u];i!=-1;i=A[i].nx){
Q.push(A[i].r);ans[A[i].r]=ans[u];
}
}
for(int i=1;i<=cnt;i++)
if(flag[i]==1&&ans[i]==0){
cnt1++;C[cnt1].d=d[i];C[cnt1].ans=0;
}
sort(C+1,C+1+cnt1,cmp1);
printf("%d\n",cnt1);
for(int i=1;i<=cnt1;i++)
printf("%d %d\n",C[i].d,C[i].ans);
}