|
@@ -0,0 +1,206 @@
|
|
|
|
|
+package leetcode.p2286;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * @ProjectName: LeetCode
|
|
|
|
|
+ * @FileName: BookMyShow
|
|
|
|
|
+ * @Author: 杨逸
|
|
|
|
|
+ * @Data:2024/10/17 9:14
|
|
|
|
|
+ * @Description: https://leetcode.cn/problems/booking-concert-tickets-in-groups/
|
|
|
|
|
+ * 2286. 以组为单位订音乐会的门票
|
|
|
|
|
+ */
|
|
|
|
|
+public class BookMyShow {
|
|
|
|
|
+ private int n,m;
|
|
|
|
|
+ private SegmentTree segmentTree;
|
|
|
|
|
+
|
|
|
|
|
+ public BookMyShow(int n, int m) {
|
|
|
|
|
+ this.n = n;
|
|
|
|
|
+ this.m = m;
|
|
|
|
|
+ segmentTree = new SegmentTree(n);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public int[] gather(int k, int maxRow) {
|
|
|
|
|
+ int index = segmentTree.index(0, 0, n - 1, maxRow, m - k);
|
|
|
|
|
+ //没有满足条件的直接返回
|
|
|
|
|
+ if (index<0)return new int[]{};
|
|
|
|
|
+
|
|
|
|
|
+ long seats = segmentTree.querySum(0, 0, n - 1, index, index);
|
|
|
|
|
+ segmentTree.update(0,0,n-1,index,k);
|
|
|
|
|
+ return new int[]{index,(int)seats};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public boolean scatter(int k, long maxRow) {
|
|
|
|
|
+ //前maxRow排剩余的座位
|
|
|
|
|
+ long leftSeats = (maxRow+1)*m - segmentTree.querySum(0,0,n-1,0,(int)maxRow);
|
|
|
|
|
+ if (leftSeats<k)return false;
|
|
|
|
|
+ //找到有空座位的排
|
|
|
|
|
+ int index = segmentTree.index(0, 0, n - 1, (int)maxRow, m - 1);
|
|
|
|
|
+ while (true){
|
|
|
|
|
+ //这排有多少空座位
|
|
|
|
|
+ leftSeats = m - segmentTree.querySum(0,0,n-1,index,index);
|
|
|
|
|
+ if (leftSeats>=k){
|
|
|
|
|
+ //这排座位能坐完人
|
|
|
|
|
+ segmentTree.update(0,0,n-1,index,k);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ //坐一部分人
|
|
|
|
|
+ segmentTree.update(0,0,n-1,index,(int)leftSeats);
|
|
|
|
|
+ k -= leftSeats;
|
|
|
|
|
+ index++;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class SegmentTree{
|
|
|
|
|
+ private long[] tree;
|
|
|
|
|
+ private long[] min;
|
|
|
|
|
+
|
|
|
|
|
+ public SegmentTree(long[] array){
|
|
|
|
|
+ int size = getSize(array.length);
|
|
|
|
|
+ this.tree = new long[size];
|
|
|
|
|
+ this.min = new long[size];
|
|
|
|
|
+ tree[0] = build(0, array.length-1, array,0);
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ public SegmentTree(int n){
|
|
|
|
|
+ int size = getSize(n);
|
|
|
|
|
+ this.tree = new long[size];
|
|
|
|
|
+ this.min = new long[size];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建线段树
|
|
|
|
|
+ * @param left
|
|
|
|
|
+ * @param right
|
|
|
|
|
+ * @param data
|
|
|
|
|
+ * @param index
|
|
|
|
|
+ * @return long
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2024/10/17 11:32:17
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ private long build(int left,int right,long[] data,int index){
|
|
|
|
|
+ if (left==right){
|
|
|
|
|
+ tree[index] = data[left];
|
|
|
|
|
+ return data[left];
|
|
|
|
|
+ }
|
|
|
|
|
+ //取中间索引
|
|
|
|
|
+ int middle = (left+right) >> 1;
|
|
|
|
|
+ //构建左子树
|
|
|
|
|
+ long leftValue = build(left, middle,data,2*index+1);
|
|
|
|
|
+ tree[2*index+1] = leftValue;
|
|
|
|
|
+ //构建右子树
|
|
|
|
|
+ long rightValue = build(middle+1, right,data,2*index+2);
|
|
|
|
|
+ tree[2*index+2] = rightValue;
|
|
|
|
|
+ tree[index] = leftValue + rightValue;
|
|
|
|
|
+ return leftValue + rightValue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新元素
|
|
|
|
|
+ * @param idx 线段树的坐标
|
|
|
|
|
+ * @param l 区间的左边界
|
|
|
|
|
+ * @param r 区间的右边界
|
|
|
|
|
+ * @param index 要更新的位置
|
|
|
|
|
+ * @param value 更新的值
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2024/10/18 09:29:00
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ public void update(int idx, int l, int r, int index, int value){
|
|
|
|
|
+ if (l==r){
|
|
|
|
|
+ tree[idx] += value;
|
|
|
|
|
+ min[idx] += value;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int mid = (l+r)>>1;
|
|
|
|
|
+
|
|
|
|
|
+ if (index<=mid){
|
|
|
|
|
+ update(idx*2+1,l,mid,index,value);
|
|
|
|
|
+ }else {
|
|
|
|
|
+ update(idx*2+2,mid+1,r,index,value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ tree[idx] = tree[2*idx+1]+tree[2*idx+2];
|
|
|
|
|
+ min[idx] = Math.min(min[2*idx+1], min[2*idx+2]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 区间查询
|
|
|
|
|
+ * @param idx 当前线段树的坐标
|
|
|
|
|
+ * @param left 当前区间的左边界
|
|
|
|
|
+ * @param right 当前区间的右边界
|
|
|
|
|
+ * @param L 查询区间的左边界
|
|
|
|
|
+ * @param R 查询区间的右边界
|
|
|
|
|
+ * @return long
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2024/10/18 09:31:25
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ public long querySum(int idx,int left,int right,int L,int R){
|
|
|
|
|
+ if (L<=left && right<=R){
|
|
|
|
|
+ return tree[idx];
|
|
|
|
|
+ }
|
|
|
|
|
+ int mid = (left+right)>>1;
|
|
|
|
|
+ long sum = 0;
|
|
|
|
|
+ if (L<=mid){
|
|
|
|
|
+ sum += querySum(idx*2+1,left,mid,L,R);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (R>mid){
|
|
|
|
|
+ sum += querySum(idx*2+2,mid+1,right,L,R);
|
|
|
|
|
+ }
|
|
|
|
|
+ return sum;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 找到第一个小于value的索引
|
|
|
|
|
+ * @param idx 当前坐标
|
|
|
|
|
+ * @param l 当前的左边界
|
|
|
|
|
+ * @param r 当前的右边界
|
|
|
|
|
+ * @param R 查询的右边界
|
|
|
|
|
+ * @param value
|
|
|
|
|
+ * @return int
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2024/10/18 11:44:18
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ public int index(int idx,int l,int r,int R,int value){
|
|
|
|
|
+ if (min[idx] > value)return -1;
|
|
|
|
|
+ if (l==r)return l;
|
|
|
|
|
+
|
|
|
|
|
+ int mid = (l+r)>>1;
|
|
|
|
|
+ //如果左子树满足条件,则递归左子树
|
|
|
|
|
+ if (min[idx*2+1] <= value)return index(idx*2+1,l,mid,R,value);
|
|
|
|
|
+ //如果右子树满足条件,则递归右子树
|
|
|
|
|
+ if (mid < R)return index(idx*2+2,mid+1,r,R,value);
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算线段树的大小
|
|
|
|
|
+ * @param n
|
|
|
|
|
+ * @return int
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: 杨逸
|
|
|
|
|
+ * @data:2024/10/21 08:51:05
|
|
|
|
|
+ * @since 1.0.0
|
|
|
|
|
+ */
|
|
|
|
|
+ private int getSize(int n){
|
|
|
|
|
+ String string = Integer.toString(n, 2).replace('0','1');
|
|
|
|
|
+ return Integer.parseInt(string, 2) << 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Your BookMyShow object will be instantiated and called as such:
|
|
|
|
|
+ * BookMyShow obj = new BookMyShow(n, m);
|
|
|
|
|
+ * int[] param_1 = obj.gather(k,maxRow);
|
|
|
|
|
+ * boolean param_2 = obj.scatter(k,maxRow);
|
|
|
|
|
+ */
|