哪个查阅窗能留空
拼多多 8月30号 笔试真题 第四题
拼多多真题目录点击查看: 拼多多 春招&秋招 笔试真题题库目录|笔试题库 + 算法考点详解
题目内容
档案室夜班要把当天的调档请求排进一排查阅窗。值班员必须先给自己留出一个空窗去核总账,再判断剩下的窗够不够分给所有调档员,并把每个窗能不能留空写进值班日志。
今晚共有 k k k 名调档员,查阅窗编号为 1 , 2 , ... , k + 1 1,2,\dots,k+1 1,2,...,k+1,一共 k + 1 k+1 k+1 个窗。第 i i i 名调档员只接受闭区间 p i , q i p_i,q_i pi,qi 里的整数编号窗。每名调档员必须恰好分到一个窗,同一个窗最多分给一名调档员。
对每个窗 t t t,请独立判断:如果窗 t t t 完全不分配、专门留给值班员,是否仍然能把全部 k k k 名调档员安排到各自能接受、且互不冲突的窗上。
请按窗号从小到大输出判断结果。判断不同的 t t t 时,可以采用完全不同的分配方案。
调档员人数满足 1 ≤ k ≤ 2 × 10 5 1 \le k \le 2 \times 10^5 1≤k≤2×105,且 1 ≤ p i ≤ q i ≤ k + 1 1 \le p_i \le q_i \le k+1 1≤pi≤qi≤k+1。
输入描述
第一行一个整数 k k k( 1 ≤ k ≤ 2 × 10 5 1 \le k \le 2 \times 10^5 1≤k≤2×105),表示调档员人数。
接下来 k k k 行,第 i i i 行两个整数 p i p_i pi、 q i q_i qi( 1 ≤ p i ≤ q i ≤ k + 1 1 \le p_i \le q_i \le k+1 1≤pi≤qi≤k+1),表示第 i i i 名调档员能接受的窗号闭区间。
输出描述
输出一个长度为 k + 1 k+1 k+1 的 01 字符串。第 t t t 个字符为 1,当且仅当存在一种合法安排,使全部调档员都分到窗、且窗 t t t 不分配给任何人;否则为 0。
样例1
输入
1
1 2
输出
11
说明
只有一名调档员,两个窗都能接受。留空窗 1 时分到 2,留空窗 2 时分到 1,两种都可行。
样例2
输入
3
1 4
2 2
3 3
输出
1001
说明
- 留空窗
1:三人可分别分到4、2、3。 - 留空窗
2:第二人只接受窗2,无法安排。 - 留空窗
3:第三人只接受窗3,无法安排。 - 留空窗
4:三人可分别分到1、2、3。
样例3
输入
3
1 2
1 2
2 2
输出
0000
说明
三人都只能挤在窗 1、2 里,这两个窗本身就装不下三个人。无论留空哪一个窗,都无法安排,因此四个位置都是 0。
题解
思路
解题思路: 贪心 + BFS + 线段树
- 贪心找出一个合法匹配。对调度员区间按照右端点升序,每个调度员尽可能分配较小位置。记录对应空闲窗口编号。
- 用线段树维护"哪些区间包含当前空窗"。
- 从初始空闲窗口进行BFS,通过交换不断扩展空窗的可达范围。如果当前空闲窗口为
x,在线段树中找到包含该窗口的区间对应的调度员,交换它原先位置至x,那么他原先占据位置即可为空闲窗口。新的空闲窗口重新加入队列中。 - 算法总体时间复杂度为
O(k log k)
C++
cpp
#include <bits/stdc++.h>
using namespace std;
struct Node {
int l, r, id;
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int k;
cin >> k;
int n = k + 1;
vector<Node> a(k);
for (int i = 0; i < k; i++) {
cin >> a[i].l >> a[i].r;
a[i].id = i;
}
// 按右端点排序,贪心寻找一个合法分配
sort(a.begin(), a.end(), [](const Node& x, const Node& y) {
return x.r < y.r;
});
set<int> s;
for (int i = 1; i <= n; i++) s.insert(i);
vector<int> match(k);
for (auto &x : a) {
auto it = s.lower_bound(x.l);
if (it == s.end() || *it > x.r) {
cout << string(n, '0');
return 0;
}
match[x.id] = *it;
s.erase(it);
}
// 唯一没有被分配的窗
int start = *s.begin();
vector<vector<int>> seg(4 * n);
function<void(int,int,int,int,int,int)> add = [&](int p, int l, int r, int ql, int qr, int id) {
if (ql <= l && r <= qr) {
seg[p].push_back(id);
return;
}
int mid = (l + r) / 2;
if (ql <= mid) add(p * 2, l, mid, ql, qr, id);
if (qr > mid) add(p * 2 + 1, mid + 1, r, ql, qr, id);
};
// 把每个调档员的可接受区间放入线段树
for (int i = 0; i < k; i++)
add(1, 1, n, a[i].l, a[i].r, a[i].id);
vector<bool> vis(n + 1);
vector<bool> used(k);
queue<int> q;
vis[start] = true;
q.push(start);
function<void(int,int,int,int)> query = [&](int p, int l, int r, int x) {
for (int id : seg[p]) {
if (used[id]) continue;
used[id] = true;
// 调档员 id 可以从原来的位置换到 x,
// 因此原来的位置就变成新的空窗
int y = match[id];
if (!vis[y]) {
vis[y] = true;
q.push(y);
}
}
if (l == r) return;
int mid = (l + r) / 2;
if (x <= mid)
query(p * 2, l, mid, x);
else
query(p * 2 + 1, mid + 1, r, x);
};
while (!q.empty()) {
int x = q.front();
q.pop();
query(1, 1, n, x);
}
for (int i = 1; i <= n; i++)
cout << vis[i];
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
static class Node {
int l, r, id;
Node(int l, int r, int id) {
this.l = l;
this.r = r;
this.id = id;
}
}
static int n;
static List<Integer>[] seg;
static boolean[] vis;
static boolean[] used;
static int[] match;
static Queue<Integer> q;
static void add(int p, int l, int r, int ql, int qr, int id) {
if (ql <= l && r <= qr) {
seg[p].add(id);
return;
}
int mid = (l + r) / 2;
if (ql <= mid)
add(p * 2, l, mid, ql, qr, id);
if (qr > mid)
add(p * 2 + 1, mid + 1, r, ql, qr, id);
}
static void query(int p, int l, int r, int x) {
for (int id : seg[p]) {
if (used[id])
continue;
used[id] = true;
// 调档员 id 可以从原来的位置换到 x,
// 因此原来的位置就变成新的空窗
int y = match[id];
if (!vis[y]) {
vis[y] = true;
q.offer(y);
}
}
if (l == r)
return;
int mid = (l + r) / 2;
if (x <= mid)
query(p * 2, l, mid, x);
else
query(p * 2 + 1, mid + 1, r, x);
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in)
);
StringBuilder input = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
input.append(line).append(' ');
}
StringTokenizer st = new StringTokenizer(input.toString());
int k = Integer.parseInt(st.nextToken());
n = k + 1;
Node[] a = new Node[k];
for (int i = 0; i < k; i++) {
int l = Integer.parseInt(st.nextToken());
int r = Integer.parseInt(st.nextToken());
a[i] = new Node(l, r, i);
}
// 按右端点排序,贪心寻找一个合法分配
Arrays.sort(a, new Comparator<Node>() {
@Override
public int compare(Node x, Node y) {
return Integer.compare(x.r, y.r);
}
});
TreeSet<Integer> s = new TreeSet<Integer>();
for (int i = 1; i <= n; i++)
s.add(i);
int[] match = new int[k];
Main.match = match;
for (Node x : a) {
Integer pos = s.ceiling(x.l);
if (pos == null || pos > x.r) {
StringBuilder ans = new StringBuilder();
for (int i = 0; i < n; i++)
ans.append('0');
System.out.println(ans);
return;
}
match[x.id] = pos;
s.remove(pos);
}
// 唯一没有被分配的窗
int start = s.first();
@SuppressWarnings("unchecked")
List<Integer>[] tree = new ArrayList[4 * n];
for (int i = 0; i < tree.length; i++)
tree[i] = new ArrayList<Integer>();
seg = tree;
// 把每个调档员的可接受区间放入线段树
for (int i = 0; i < k; i++) {
add(
1,
1,
n,
a[i].l,
a[i].r,
a[i].id
);
}
vis = new boolean[n + 1];
used = new boolean[k];
q = new ArrayDeque<Integer>();
vis[start] = true;
q.offer(start);
while (!q.isEmpty()) {
int x = q.poll();
query(1, 1, n, x);
}
StringBuilder ans = new StringBuilder();
for (int i = 1; i <= n; i++) {
if (vis[i])
ans.append('1');
else
ans.append('0');
}
System.out.println(ans);
}
}
python
python
import sys
import bisect
from collections import deque
input = sys.stdin.readline
k = int(input())
n = k + 1
a = []
for i in range(k):
l, r = map(int, input().split())
a.append([l, r, i])
# 按右端点排序,贪心寻找一个合法分配
a.sort(key=lambda x: x[1])
s = list(range(1, n + 1))
match = [0] * k
for l, r, idx in a:
pos = bisect.bisect_left(s, l)
if pos == len(s) or s[pos] > r:
print('0' * n)
sys.exit()
match[idx] = s[pos]
s.pop(pos)
# 唯一没有被分配的窗
start = s[0]
seg = [[] for _ in range(4 * n)]
def add(p, l, r, ql, qr, idx):
if ql <= l and r <= qr:
seg[p].append(idx)
return
mid = (l + r) // 2
if ql <= mid:
add(p * 2, l, mid, ql, qr, idx)
if qr > mid:
add(p * 2 + 1, mid + 1, r, ql, qr, idx)
# 把每个调档员的可接受区间放入线段树
for l, r, idx in a:
add(1, 1, n, l, r, idx)
vis = [False] * (n + 1)
used = [False] * k
q = deque()
vis[start] = True
q.append(start)
def query(p, l, r, x):
for idx in seg[p]:
if used[idx]:
continue
used[idx] = True
# 调档员 id 可以从原来的位置换到 x,
# 因此原来的位置就变成新的空窗
y = match[idx]
if not vis[y]:
vis[y] = True
q.append(y)
if l == r:
return
mid = (l + r) // 2
if x <= mid:
query(p * 2, l, mid, x)
else:
query(p * 2 + 1, mid + 1, r, x)
while q:
x = q.popleft()
query(1, 1, n, x)
print(''.join('1' if vis[i] else '0' for i in range(1, n + 1)))
javascript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
crlfDelay: Infinity
});
const input = [];
rl.on('line', line => {
input.push(...line.trim().split(/\s+/));
});
rl.on('close', () => {
let idx = 0;
const k = Number(input[idx++]);
const n = k + 1;
const a = [];
for (let i = 0; i < k; i++) {
const l = Number(input[idx++]);
const r = Number(input[idx++]);
a.push({ l, r, id: i });
}
// 按右端点排序,贪心寻找一个合法分配
a.sort((x, y) => x.r - y.r);
const s = [];
for (let i = 1; i <= n; i++) s.push(i);
const match = new Array(k).fill(0);
for (const x of a) {
let left = 0, right = s.length;
while (left < right) {
const mid = (left + right) >> 1;
if (s[mid] >= x.l)
right = mid;
else
left = mid + 1;
}
if (left === s.length || s[left] > x.r) {
console.log('0'.repeat(n));
return;
}
match[x.id] = s[left];
s.splice(left, 1);
}
// 唯一没有被分配的窗
const start = s[0];
const seg = Array.from({ length: 4 * n }, () => []);
function add(p, l, r, ql, qr, id) {
if (ql <= l && r <= qr) {
seg[p].push(id);
return;
}
const mid = (l + r) >> 1;
if (ql <= mid)
add(p * 2, l, mid, ql, qr, id);
if (qr > mid)
add(p * 2 + 1, mid + 1, r, ql, qr, id);
}
// 把每个调档员的可接受区间放入线段树
for (const x of a)
add(1, 1, n, x.l, x.r, x.id);
const vis = new Array(n + 1).fill(false);
const used = new Array(k).fill(false);
const q = [];
vis[start] = true;
q.push(start);
function query(p, l, r, x) {
for (const id of seg[p]) {
if (used[id]) continue;
used[id] = true;
// 调档员 id 可以从原来的位置换到 x,
// 因此原来的位置就变成新的空窗
const y = match[id];
if (!vis[y]) {
vis[y] = true;
q.push(y);
}
}
if (l === r) return;
const mid = (l + r) >> 1;
if (x <= mid)
query(p * 2, l, mid, x);
else
query(p * 2 + 1, mid + 1, r, x);
}
let head = 0;
while (head < q.length) {
const x = q[head++];
query(1, 1, n, x);
}
let ans = '';
for (let i = 1; i <= n; i++)
ans += vis[i] ? '1' : '0';
console.log(ans);
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
"sort"
)
type Node struct {
l, r, id int
}
var n int
var seg [][]int
var vis []bool
var used []bool
var match []int
var q []int
func add(p, l, r, ql, qr, id int) {
if ql <= l && r <= qr {
seg[p] = append(seg[p], id)
return
}
mid := (l + r) / 2
if ql <= mid {
add(p*2, l, mid, ql, qr, id)
}
if qr > mid {
add(p*2+1, mid+1, r, ql, qr, id)
}
}
func query(p, l, r, x int) {
for _, id := range seg[p] {
if used[id] {
continue
}
used[id] = true
// 调档员 id 可以从原来的位置换到 x,
// 因此原来的位置就变成新的空窗
y := match[id]
if !vis[y] {
vis[y] = true
q = append(q, y)
}
}
if l == r {
return
}
mid := (l + r) / 2
if x <= mid {
query(p*2, l, mid, x)
} else {
query(p*2+1, mid+1, r, x)
}
}
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
var k int
fmt.Fscan(in, &k)
n = k + 1
a := make([]Node, k)
for i := 0; i < k; i++ {
fmt.Fscan(in, &a[i].l, &a[i].r)
a[i].id = i
}
// 按右端点排序,贪心寻找一个合法分配
sort.Slice(a, func(i, j int) bool {
return a[i].r < a[j].r
})
s := make([]int, n)
for i := 0; i < n; i++ {
s[i] = i + 1
}
match = make([]int, k)
for _, x := range a {
pos := sort.SearchInts(s, x.l)
if pos == len(s) || s[pos] > x.r {
for i := 0; i < n; i++ {
fmt.Fprint(out, "0")
}
return
}
match[x.id] = s[pos]
s = append(s[:pos], s[pos+1:]...)
}
// 唯一没有被分配的窗
start := s[0]
seg = make([][]int, 4*n)
// 把每个调档员的可接受区间放入线段树
for _, x := range a {
add(1, 1, n, x.l, x.r, x.id)
}
vis = make([]bool, n+1)
used = make([]bool, k)
q = make([]int, 0)
vis[start] = true
q = append(q, start)
head := 0
for head < len(q) {
x := q[head]
head++
query(1, 1, n, x)
}
for i := 1; i <= n; i++ {
if vis[i] {
fmt.Fprint(out, "1")
} else {
fmt.Fprint(out, "0")
}
}
}