加农炮 51Nod - 1287(线段树)
一个长度为M的正整数数组A,表示从左向右的地形高度。测试一种加农炮,炮弹平行于地面从左向右飞行,高
度为H,如果某处地形的高度大于等于炮弹飞行的高度H(Ai >= H),炮弹会被挡住并落在i - 1处,则Ai−1 + 1。
如果H <= A0,则这个炮弹无效,如果H > 所有的Ai,这个炮弹也无效。现在给定N个整数的数组B代表炮弹高
度,计算出最后地形的样子。例如:地形高度A = {1, 2, 0, 4, 3, 2, 1, 5, 7},
炮弹高度B = {2, 8, 0, 7, 6, 5, 3, 4, 5, 6, 5},最终得到的地形高度为:{2, 2, 2, 4, 3, 3, 5, 6, 7}。
Input:
第1行:2个数M, N中间用空格分隔,分别为数组A和B的长度(1 <= m, n <= 50000)
第2至M + 1行:每行1个数,表示对应的地形高度(0 <= Ai <= 1000000)。
第M + 2至N + M + 1行,每行1个数,表示炮弹的高度(0 <= Bi <= 1000000)。
Output:
输出共M行,每行一个数,对应最终的地形高度。
Sample Input:
9 11
1
2
0
4
3
2
1
5
7
2
8
0
7
6
5
3
4
5
6
5
Sample Output:
2
2
2
4
3
3
5
6
7
思路:
这个题数据给的不严,暴力有很多种方法可以跑过去,不过还是推荐线段树。
没啥坑点,就是注意点更新。
因为找到的点往往是第一个>=炮弹的点而要更新的是前一个点。不能直接通过下标减一来更新。
因为会有几种特殊情况:
如图中的【4,4】点和【2,2】点还有【6,6】点和【5,5】 点,后一个点无法通过下标减一来找到前一个点。
所以推荐用数组记录下每个叶子对应的下标。
代码:
#include<stdio.h>
#include<iostream>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
int board[50005][2];//存地形高度和对应的在map里的下标
int map[700005];
int m,n;
void Build(int head,int tail,int now)
{
if(head == tail)
{
map[now] = board[head][0];
board[head][1] = now;
return ;
}
int mid = (head+tail)/2;
Build(head,mid,now<<1);
Build(mid+1,tail,now<<1|1);
map[now] = max(map[now<<1],map[now<<1|1]);
}
void p(int now) //用于叶结点更新后更新线段树
{
if(now == 1)return ;
if(now&1)
{
if(map[now]>map[(now-1)/2])
{
map[(now-1)/2] = map[now];
p((now-1)/2);
}
}
else
{
if(map[now]>map[now/2])
{
map[now/2] = map[now];
p(now/2);
}
}
}
void Updata(int head,int tail,int now,int val)
{
if(head == tail)
{
if(head == 1)return;
board[head-1][0]++;
map[board[head-1][1]] = board[head-1][0];
p(board[head-1][1]);
return ;
}
int mid = (head+tail)/2;
if(map[now<<1]>=val)Updata(head,mid,now<<1,val);
else Updata(mid+1,tail,now<<1|1,val);
}
int main()
{
scanf("%d %d",&m,&n);
for(int i=1 ; i<=m ; i++)
{
scanf("%d",&board[i][0]);
}
Build(1,m,1);
while(n--)
{
int mid;
scanf("%d",&mid);
if(mid>map[1])continue;
Updata(1,m,1,mid);
}
for(int i=1 ; i<m ; i++)printf("%d\n",board[i][0]);
printf("%d",board[m][0]);
return 0;
}