一、内容

计蒜客---公告板_线段树计蒜客---公告板_线段树_02

二、思路

  • 线段树的水题,但需要注意的是我们的区间有多大。题目给了一个h和n块板子,所以我们只需要在h 和 n中取一个最小值就是区间的大小了,因为每块板子高度是1,若h > n,那么区间取【1,n】就够了,因为每块板只放一行都只能放n行。
  • 我们用c[] 数组来保存某个区间的最大剩余值的下标,这样我们查询那行能够放下这块板子,如果左边的区间能够放下这块板子,那么就往左边递归,反之则往右边递归。
  • a[] 数组保存每行剩余的宽度,初始都是w。
    	static int query(int id, int lef, int r, int v) {
       	if (lef == r) {
       		return lef;
       	}
       	int mid = (lef + r) >> 1;
       	int ans = -1;
       	if (a[c[id << 1]] >= v) {
       		//向左边走  左边的最大值大于v
       		ans = query(id << 1, lef, mid, v);
       	} else if (a[c[id << 1 | 1]] >= v) {
       		//向右边走 
       		ans = query(id << 1 | 1, mid + 1, r, v);
       	}
       	return ans;
       }
    
  • 更新操作,每放下一块板都要对线段树进行更新,将放置板子的一行的剩余w减去板子宽度。更新相连的区间。
       static void update(int id, int lef, int r,int x, int v) {
       		if (lef == r) {
       			a[x] -= v;
       			return;
       		}
       		int mid = (lef + r) >> 1;
       		if (x <= mid) {
       			update(id << 1, lef, mid, x, v);
       		} else {
       			update(id << 1 | 1, mid + 1, r, x, v);
       		}
       		//进行up  更新区间的  拥有值的最大小标
       		if (a[c[id << 1]] >= a[c[id << 1 | 1]]) {
       			c[id] = c[id << 1];
       		} else {
       			c[id] = c[id << 1 | 1];
       		}
       }
    

三、代码

import java.util.Scanner;

public class C_公告板 {
	static int[] a = new int[200005]; //因为最多只有那么多个板子 高度不可能超过200000
	static int[] c = new int[200005];
	static int h, w, n, INF = 0X7f7f7f7f;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		h = sc.nextInt();
		w = sc.nextInt();
		n = sc.nextInt();
		h = Math.min(h, n);
		for (int i = 1; i <= h; i++) {
			a[i] = w;
		}
		build(1, 1, h);
		int v;
		while (n-- > 0) {
			v = sc.nextInt();
			int x = query(1, 1, h, v);
			System.out.println(x);
			if (x != - 1) update(1, 1, h, x, v);
		}
	}
	static void update(int id, int lef, int r,int x, int v) {
		if (lef == r) {
			a[x] -= v;
			return;
		}
		int mid = (lef + r) >> 1;
		if (x <= mid) {
			update(id << 1, lef, mid, x, v);
		} else {
			update(id << 1 | 1, mid + 1, r, x, v);
		}
		//进行up  更新区间的  拥有值的最大小标
		if (a[c[id << 1]] >= a[c[id << 1 | 1]]) {
			c[id] = c[id << 1];
		} else {
			c[id] = c[id << 1 | 1];
		}
	}
	static void build(int id, int lef, int r) {
		if (lef == r) {
			c[id] = lef;
			return;
		}
		int mid = (lef + r) >> 1;
		build(id << 1, lef, mid);
		build(id << 1 | 1, mid + 1, r);
		//up(id);
		if (a[c[id << 1]] >= a[c[id << 1 | 1]]) {
			c[id] = c[id << 1];
		} else {
			c[id] = c[id << 1 | 1];
		}
	}
	static int query(int id, int lef, int r, int v) {
		if (lef == r) {
			return lef;
		}
		int mid = (lef + r) >> 1;
		int ans = -1;
		if (a[c[id << 1]] >= v) {
			//向左边走  左边的最大值大于v
			ans = query(id << 1, lef, mid, v);
		} else if (a[c[id << 1 | 1]] >= v) {
			//向右边走 
			ans = query(id << 1 | 1, mid + 1, r, v);
		}
		return ans;
	}
}