链接:https://ac.nowcoder.com/acm/contest/945/I
来源:牛客网
题目描述
To earn some extra money, the cows have opened a restaurant in their barn specializing in milkshakes. The restaurant has N seats (1 <= N <= 500,000) in a row. Initially, they are all empty.
Throughout the day, there are M different events that happen in sequence at the restaurant (1 <= M <= 300,000). The two types of events that can happen are:
- A party of size p arrives (1 <= p <= N). Bessie wants to seat the party in a contiguous block of p empty seats. If this is possible, she does so in the lowest position possible in the list of seats. If it is impossible, the party is turned away.
- A range [a,b] is given (1 <= a <= b <= N), and everybody in that range of seats leaves. Please help Bessie count the total number of parties that are turned away over the course of the day.
输入描述:
-
Line 1: Two space-separated integers, N and M.
-
Lines 2…M+1: Each line describes a single event. It is either a
line of the form “A p” (meaning a party of size p arrives) or
“L a b” (meaning that all cows in the range [a, b] leave).
输出描述:
- Line 1: The number of parties that are turned away.
输入
10 4
A 6
L 2 4
A 5
A 2
输出
1
INPUT DETAILS:
There are 10 seats, and 4 events. First, a party of 6 cows arrives. Then
all cows in seats 2…4 depart. Next, a party of 5 arrives, followed by a
party of 2.
OUTPUT DETAILS:
Party #3 is turned away. All other parties are seated.
解题思路
这题做了我半天。。。。
想起大一的时候小学期做过同样的题目,不过那个时候得到数据规模是很小的,所以当时的我是暴力做的。
拿到这个题,第一个想到线段树,去记录区间可用的seat有多少个。这么做还不够,还需要能找到安排时最左的满足条件的坐标。
那线段树就需要再多记录两个元素。一个是从左往右数空闲位置的个数,另一个是从右往左树空闲位置的个数。
说的再多,也不如代码清楚:
AC Code:
/*
* Copyright (c) 2019 Ng Kimbing, HNU, All rights reserved. May not be used, modified, or copied without permission.
* @Author: Ng Kimbing, HNU.
* @LastModified:2019-06-25 T 16:16:48.826 +08:00
*/
package ACMProblems.QianDaoTi;
import static ACMProblems.ACMIO.*;
public class Main {
private static final int maxN = 500000 + 10;
private static final int REMOVE_ALL = 1;
private static final int PLACE = 2;
static class Node {
int numFromLeft;
int numFromRight;
int value;
int lazyType;
public Node(int numFromLeft, int numFromRight, int value, int lazyType) {
this.numFromLeft = numFromLeft;
this.numFromRight = numFromRight;
this.value = value;
this.lazyType = lazyType;
}
Node() {}
}
private static Node[] nodes = new Node[maxN * 4];
private static void pushUp(int l, int r, int curr) {
updateLeftRight(l, r, curr);
// considering merge sort
nodes[curr].value = Math.max(
Math.max(nodes[curr << 1].value, nodes[curr << 1 | 1].value),
nodes[curr << 1].numFromRight + nodes[curr << 1 | 1].numFromLeft
);
}
private static void updateLeftRight(int l, int r, int curr) {
int mid = (l + r) >> 1;
nodes[curr].numFromLeft = (nodes[curr << 1].value == (mid - l + 1)) ?
nodes[curr << 1].value + nodes[curr << 1 | 1].numFromLeft :
nodes[curr << 1].numFromLeft;
nodes[curr].numFromRight = (nodes[curr << 1 | 1].value == (r - mid)) ?
nodes[curr << 1 | 1].value + nodes[curr << 1].numFromRight :
nodes[curr << 1 | 1].numFromRight;
}
private static void pushDown(int l, int r, int curr) {
nodes[curr << 1].lazyType = nodes[curr << 1 | 1].lazyType = nodes[curr].lazyType;
int m = (l + r) >> 1;
if (nodes[curr].lazyType == REMOVE_ALL) {
nodes[curr << 1].numFromLeft = nodes[curr << 1].numFromRight = nodes[curr << 1].value = m - l + 1;
nodes[curr << 1 | 1].numFromLeft = nodes[curr << 1 | 1].numFromRight = nodes[curr << 1 | 1].value = r - m;
} else if (nodes[curr].lazyType == PLACE) {
nodes[curr << 1].numFromLeft = nodes[curr << 1].numFromRight = nodes[curr << 1].value = 0;
nodes[curr << 1 | 1].numFromLeft = nodes[curr << 1 | 1].numFromRight = nodes[curr << 1 | 1].value = 0;
}
nodes[curr].lazyType = 0;
}
private static void build(int left, int right, int curr) {
if (left == right) {
if (nodes[curr] == null) {
nodes[curr] = new Node(1, 1, 1, 0);
return;
}
nodes[curr].numFromLeft = nodes[curr].numFromRight = nodes[curr].value = 1;
return;
}
int mid = (left + right) >> 1;
build(left, mid, curr << 1);
build(mid + 1, right, curr << 1 | 1);
nodes[curr] = new Node();
pushUp(left, right, curr);
}
private static void update(int targetL, int targetR, int operationType, int l, int r, int curr) {
if (nodes[curr].lazyType != 0 && curr * 2 + 1 < nodes.length)
pushDown(l, r, curr);
// the whole interval
if (targetL <= l && r <= targetR) {
if (operationType == REMOVE_ALL)
nodes[curr].numFromLeft = nodes[curr].numFromRight = nodes[curr].value = r - l + 1;
if (operationType == PLACE)
nodes[curr].numFromLeft = nodes[curr].numFromRight = nodes[curr].value = 0;
nodes[curr].lazyType = operationType;
return;
}
int mid = (l + r) >> 1;
if (targetL <= mid) update(targetL, targetR, operationType, l, mid, curr << 1);
if (targetR > mid) update(targetL, targetR, operationType, mid + 1, r, curr << 1 | 1);
pushUp(l, r, curr);
}
private static int query(int requiredSeats, int left, int right, int curr) {
if (nodes[curr].lazyType != 0 && curr * 2 + 1 < nodes.length)
pushDown(left, right, curr);
if (left == right)
return left;
int m = (left + right) >> 1;
// There are enough seats in the left part
if (nodes[curr << 1].value >= requiredSeats)
return query(requiredSeats, left, m, curr << 1);
// Cross two sub-intervals
else if (nodes[curr << 1].numFromRight + nodes[curr << 1 | 1].numFromLeft >= requiredSeats)
return m - nodes[curr << 1].numFromRight + 1;
// try to look for it in the right part
else
return query(requiredSeats, m + 1, right, curr << 1 | 1);
}
public static void main(String[] args) throws Exception {
setStream(System.in);
for (int i = 0; i < nodes.length; ++i)
nodes[i] = new Node();
int n = nextInt();
int m = nextInt();
build(1, n, 1);
int num = 0;
for (int i = 1; i <= m; i++) {
char cmd = nextChar();
if (cmd == 'A') {
int x = nextInt();
if (nodes[1].value >= x) {
int ans = query(x, 1, n, 1);
update(ans, ans + x - 1, PLACE, 1, n, 1);
} else num++;
} else {
int l = nextInt();
int r = nextInt();
update(l, r, REMOVE_ALL, 1, n, 1);
}
}
System.out.println(num);
}
}
这题很奇葩,上述代码在OJ上交就是超时,(但实际上并不会超时)应该是OJ的问题
因此我对OJ进行了测试:
题目给的是两秒钟的时间, 提交如下代码:
public class Main{
static Main[]mains= new Main[(int)(500000*4)];
public static void main(String[]args)throws Exception{
long start = System.currentTimeMillis();
for(int i = 0; i < 2000000; ++i){
mains[i] = new Main();
long end = System.currentTimeMillis();
if(i % 10000 == 0)
System.out.println(i + ", time duration: "+(end - start));
}
long end = System.currentTimeMillis();
System.out.println((end-start));
}
}
输出结果的最后几行为
判题结果为:
有整整1.5秒的时间并不能得到利用,跑了半秒就停了。。
(怀疑可能和内存之类的方面有关, 因为当将循环内的new语句改为简单的加法,增大循环次数,可以跑到两秒,但如上的代码,怎么也超不过半秒就无法继续进行。)
OJ可能对JAVA 有什么偏见
因此改成了C++版本就顺利AC了
C++ AC Code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 500000 + 10;
// static const int maxn = 15;
const int REMOVE_ALL = 1;
const int PLACE = 2;
const int mi = maxn*3;
class Node {
public:
int numFromLeft;
int numFromRight;
int value;
int lazyType;
Node(int numFromLeft, int numFromRight, int value, int lazyType) {
this->numFromLeft = numFromLeft;
this->numFromRight = numFromRight;
this->value = value;
this->lazyType = lazyType;
}
Node() {}
};
Node nodes[maxn * 4];
// static {
// for (int i = 0; i < nodes.length; ++i)
// nodes[i] = new Node();
// }
void updateLeftRight(int l, int r, int curr) {
int mid = (l + r) >> 1;
nodes[curr].numFromLeft = (nodes[curr << 1].value == (mid - l + 1)) ?
nodes[curr << 1].value + nodes[curr << 1 | 1].numFromLeft :
nodes[curr << 1].numFromLeft;
nodes[curr].numFromRight = (nodes[curr << 1 | 1].value == (r - mid)) ?
nodes[curr << 1 | 1].value + nodes[curr << 1].numFromRight :
nodes[curr << 1 | 1].numFromRight;
}
void pushUp(int l, int r, int curr) {
updateLeftRight(l, r, curr);
// considering merge sort
nodes[curr].value = max(
max(nodes[curr << 1].value, nodes[curr << 1 | 1].value),
nodes[curr << 1].numFromRight + nodes[curr << 1 | 1].numFromLeft
);
}
void pushDown(int l, int r, int curr) {
nodes[curr << 1].lazyType = nodes[curr << 1 | 1].lazyType = nodes[curr].lazyType;
int m = (l + r) >> 1;
if (nodes[curr].lazyType == REMOVE_ALL) {
nodes[curr << 1].numFromLeft = nodes[curr << 1].numFromRight = nodes[curr << 1].value = m - l + 1;
nodes[curr << 1 | 1].numFromLeft = nodes[curr << 1 | 1].numFromRight = nodes[curr << 1 | 1].value = r - m;
} else if (nodes[curr].lazyType == PLACE) {
nodes[curr << 1].numFromLeft = nodes[curr << 1].numFromRight = nodes[curr << 1].value = 0;
nodes[curr << 1 | 1].numFromLeft = nodes[curr << 1 | 1].numFromRight = nodes[curr << 1 | 1].value = 0;
}
nodes[curr].lazyType = 0;
}
void build(int left, int right, int curr) {
if (left == right) {
nodes[curr].numFromLeft = nodes[curr].numFromRight = nodes[curr].value = 1;
return;
}
int mid = (left + right) >> 1;
build(left, mid, curr << 1);
build(mid + 1, right, curr << 1 | 1);
pushUp(left, right, curr);
}
void update(int targetL, int targetR, int operationType, int l, int r, int curr) {
if (nodes[curr].lazyType != 0 && curr * 2 + 1 < mi)
pushDown(l, r, curr);
// the whole interval
if (targetL <= l && r <= targetR) {
if (operationType == REMOVE_ALL)
nodes[curr].numFromLeft = nodes[curr].numFromRight = nodes[curr].value = r - l + 1;
if (operationType == PLACE)
nodes[curr].numFromLeft = nodes[curr].numFromRight = nodes[curr].value = 0;
nodes[curr].lazyType = operationType;
return;
}
int mid = (l + r) >> 1;
if (targetL <= mid) update(targetL, targetR, operationType, l, mid, curr << 1);
if (targetR > mid) update(targetL, targetR, operationType, mid + 1, r, curr << 1 | 1);
pushUp(l, r, curr);
}
int query(int requiredSeats, int left, int right, int curr) {
if (nodes[curr].lazyType != 0)
pushDown(left, right, curr);
if (left == right)
return left;
int m = (left + right) >> 1;
// There are enough seats in the left part
if (nodes[curr << 1].value >= requiredSeats)
return query(requiredSeats, left, m, curr << 1);
// Cross two sub-intervals
else if (nodes[curr << 1].numFromRight + nodes[curr << 1 | 1].numFromLeft >= requiredSeats)
return m - nodes[curr << 1].numFromRight + 1;
// try to look for it in the right part
else
return query(requiredSeats, m + 1, right, curr << 1 | 1);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
// long start = System.currentTimeMillis();
// for (int i = 0; i < nodes.length; ++i)
// nodes[i] = new Node();
// long end = System.currentTimeMillis();
// System.out.println((end - start));
// System.exit(-1);
cin >> n >> m;
build(1, n, 1);
// int foo =(int) (Math.log(n)/Math.log(2) + 1) * 2;
// System.out.println(foo);
// System.exit(0);
int num = 0;
for (int i = 1; i <= m; i++) {
char cmd;
cin >> cmd;
if (cmd == 'A') {
int x;
cin >> x;
if (nodes[1].value >= x) {
int ans = query(x, 1, n, 1);
update(ans, ans + x - 1, PLACE, 1, n, 1);
} else num++;
} else {
int l, r;
cin >> l >> r;
update(l, r, REMOVE_ALL, 1, n, 1);
}
}
cout << num << endl;
}