传送门:51nod 1276
题目大意:由于是中文题就不再描述了。
Input示例
5 4
2
1
3
2
3
0
1
3
2
Output示例
1
2
0
2
思路:
第一思路肯定是暴力的方法,每次询问都去查询被淹没的岛屿和不相连的岛屿数,这样的时间复杂度是O(n)*O(q),会超时。网上的思路一般都是离线处理,也就是说针对每个询问不立刻给出答案,而是等询问全部给出后一起给出答案。由于水会先淹没高度低的岛屿,所以如果先处理高度低的岛屿再处理高度高的就避免了重复计算,大大的降低了时间复杂度。
经过观察我们发现,只有淹没的岛屿为山峰(比两边都高)的时候,原来的岛屿数要-1,如果淹没的是山谷(比两边都低),一个岛变成两个,则原来的岛屿数+1. 这里要注意首尾两个岛屿比较特殊,如果它们是孤立的岛屿的话,淹没它们则岛屿数+1,否则不变化。
这样一来,我们找到了方法也找到了规律。不过还有几个难点:
1.既然要对岛屿和询问从低到高处理,那么就要对其排序,怎么确定在原数组中的位置呢?很简单,用结构体记录位置就可以了。
2.怎么判断为山峰还是山谷,我们注意到可能存在这样的数据:1 6 6 6 1,如果单纯的判断是否比两边高或比两边低是不行的。这里就有一个很巧妙的方法:当该岛屿要被淹没时,如果两边的岛屿都没被淹没,则为山谷;如果两边的岛屿都被淹没,则为山峰。
#include<stdio.h>
#include<string.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
int val,pos; //记录值和位置
} a[50010],q[50010];
int cmp(node x,node y)
{
return x.val<y.val;
}
int main()
{ //f为0表示未被淹没,为1表示已被淹没
int i,j,n,m,sum,f[50010],ans[50010];
while(~scanf("%d%d",&n,&m))
{
for(i=0;i<n;i++)
{
scanf("%d",&a[i].val);
a[i].pos=i;
}
sort(a,a+n,cmp);
for(i=0;i<m;i++)
{
scanf("%d",&q[i].val);
q[i].pos=i;
}
sort(q,q+m,cmp);
j=0;
sum=1; //sum记录岛屿数,初始时只有一个岛屿
memset(f,0,sizeof(f));
for(i=0;i<m;i++)
{ //针对从小到大的每个询问
while(a[j].val<=q[i].val&&j<n)
{ //扫描当前水平面可以淹没的岛屿
if(a[j].pos==0)
{ //如果是第一个岛
if(f[a[j].pos+1]==1) sum--;
}
else if(a[j].pos==n-1)
{ //如果是最后一个岛
if(f[a[j].pos-1]==1) sum--;
}
else
{
if(f[a[j].pos+1]&&f[a[j].pos-1]) sum--; //如果两边都被淹没,则为山峰
else if(!f[a[j].pos+1]&&!f[a[j].pos-1]) sum++; //如果两边都未被淹没,则为山谷
}
f[a[j].pos]=1; //当前岛屿淹没
j++;
}
ans[q[i].pos]=sum; //记录答案
}
for(i=0;i<m;i++) printf("%d\n",ans[i]);
}
return 0;
}