POJ 3470 线段树 + 扫描线法

大致题意

有N堵水平于坐标轴的墙,以及M只小鸟,小鸟一定会朝离自己最近的墙飞去撞晕自己,且只能飞向平行于坐标轴的 4个方向,撞到墙的边缘也算合理冲撞(即是说可以往延长线飞)。求各堵墙上各撞了几只小鸟。N, M <= 50000。

用线段树维护墙的索引值,对应于小鸟的4个方向,分别在x, y轴上正向和反向扫描,更新离小鸟最近的墙。

扫描线法,即对于某一个方向按坐标值顺序扫描。因为此处可在延长线上飞,若对x轴扫描,对于墙两个端点y坐标值相等(即考虑延长线)和y坐标值不等都对线段树进行更新。

题中没有给出坐标值范围,考虑到线段树的维护,要先坐标离散化。本来用vector的erase(), unique()来做离散化,但这里需要保留原值计算最小距离,还要考虑到x, y的对应关系,码量略大。然后发现了手动坐标离散化的方法:按照坐标值对索引做排序,然后可以得到保留原顺序的离散坐标。用索引做,离散值、原值以及x, y直接对应,妙啊。

对于墙而言,成对输入的坐标,若保存在相邻位置,可以通过索引异或1方便的求出另一个坐标值。

#include <cstdio>
#include <STDLIB.H>
#include <algorithm>
#include <iostream>
#define min(a,b)    (((a) < (b)) ? (a) : (b))
#define max(a,b)    (((a) > (b)) ? (a) : (b))
#define abs(x)    ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define eps 1e-4
#define M_PI 3.14159265358979323846
#define MAX_A 150015
#define MAX_M 50005
#define MAX_N 50005
using namespace std;
int N, M, ax_size, w_size;
int X[MAX_A], Y[MAX_A];
int sx[MAX_A], sy[MAX_A], rx[MAX_A], ry[MAX_A];
//坐标离散化
int* T;   
bool cmp(int x, int y){
	return T[x] < T[y];
}
void compress(int* X, int* sx, int* rx){
	for(int i = 0; i < ax_size; i++) rx[i] = i;
	T = X;
	sort(rx, rx + ax_size, cmp);
	sx[rx[0]] = 0;
	for(int j = 1; j < ax_size; j++){
		int now = rx[j], pre = rx[j - 1];
		if(X[now] > X[pre]) sx[now] = sx[pre] + 1;
		else sx[now] = sx[pre];
	}
}
void init(){
	w_size = N << 1, ax_size = w_size + M;
	for(int i = 0; i < ax_size; i++) scanf("%d%d", X + i, Y + i);
	compress(X, sx, rx);
	compress(Y, sy, ry);
}

const int ST_SIZE = (1 << 19) - 1;
int wall[ST_SIZE];
int chs[MAX_M], dis[MAX_M], cnt[MAX_N];
//墙的索引线段树
void update(int a, int b, int id, int k, int l, int r){
	if(b <= l || a >= r) return;
	else if(a <= l && r <= b) wall[k] = id;
	else{
		int chl = (k << 1) + 1, chr = (k << 1) + 2, m = (l + r) >> 1;
		if(wall[k] > -2) {
			wall[chl] = wall[chr] = wall[k];
			wall[k] = -2;
		}
		update(a, b, id, chl, l, m);
		update(a, b, id, chr, m, r);
	}
}
int query(int a, int b, int k, int l, int r){
	if(wall[k] > -2) return wall[k];
	else{
		int chl = (k << 1) + 1, chr = (k << 1) + 2, m = (l + r) >> 1;
		if(a < m) return query(a, b, chl, l, m);
		if(a >= m) return query(a, b, chr, m, r);
	}
}
void line_sweep(int* sy, int* X, int H, int i){
	if(i < w_size){//墙
		int i2 = i ^ 1, id = i >> 1;
		if(sy[i] <= sy[i2]){ //对于sy[i] != sy[i2]情况只更新一次线段树
			update(sy[i], sy[i2] + 1, id, 0, 0, H);
		}
	}
	else{//小鸟
		int id = query(sy[i], sy[i] + 1, 0, 0, H);
		if(id >= 0){
			int bx = X[i], x1 = X[id << 1], x2 = X[(id << 1) | 1];
			int d = min(abs(bx - x1), abs(bx - x2));
			i -= w_size;
			if(dis[i] > d || chs[i] == -1){
				dis[i] = d, chs[i] = id;
			}
		}
	}
}
void sweep(int* sy, int* rx, int* X, int H){
	update(0, H, -1, 0, 0, H);
	for(int x = 0; x < ax_size; x++) line_sweep(sy, X, H, rx[x]);
	update(0, H, -1, 0, 0, H);
	for(int x = ax_size - 1; x >= 0; x--) line_sweep(sy, X, H, rx[x]);
}
void solve(){
	//计算x, y轴离散化后区间
	int H = sy[ry[ax_size - 1]] + 1, W = sx[rx[ax_size - 1]] + 1;
	memset(chs, -1, sizeof(chs));
	memset(cnt, 0, sizeof(cnt));
	sweep(sy, rx, X, H); //扫描x轴
	sweep(sx, ry, Y, W); //扫描y轴
	for(int i = 0; i < M; i++) ++cnt[chs[i]];
	for(int j = 0; j < N; j++) printf("%d\n", cnt[j]);
}
int main(){
	while(~scanf("%d%d", &N, &M)){
		init();
		solve();
	}
	return 0;
}
发布了6 篇原创文章 · 获赞 0 · 访问量 48

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/104333611