题目笔记——P16540 [EGOI 2026] 披萨大师 / Ovenmasters
P16540 [EGOI 2026] 披萨大师 / Ovenmasters
题目大意
有 \(N\) 个唯一排名(\(0\) 最好,\(N-1\) 最差)的师傅依次到达,前 \(M\) 个直接占满 \(M\) 张桌子并放下披萨,之后每人要吃掉某张桌上“比自己好但最差”的披萨并留下自己的,如果没有符合条件的披萨则直接离开。已知每张桌子从下到上记录的每个放在过这个桌子上的披萨排名序列,要求还原一个可能的到达顺序(没出现在记录里的就是直接离开的人),若有多个则输出字典序最小的,若不存在则输出 NO。
解题思路
关键点:
- 同一张桌子的记录在合法情况下每个披萨的排名应为递增。
- 两两桌子上披萨排名之间的大小关系保持不变。
让我们先来证明一下:
一、同一张桌子的记录在合法情况下每个披萨的排名应为递增。
每次用披萨 \(a\) 替换 \(b\) 都要保证 \(b\) 为小于 \(a\) 的最大值,那么 \(a\) 永远比 \(b\) 大。
二、两两桌子上披萨排名之间的大小关系保持不变。

如图,要保证当前用披萨 \(a\) 替换 \(b\),就要先替换完 \(a\) 与 \(b\) 之间的所有披萨,最终替换完后两两之间的大小关系保持不变。
于是,我们可以将桌子按照大小关系从小到大排序并依次替换。
但是,每次将当前替换后前面的披萨也有可能可以替换了,于是我们可以递归替换完所有前面的披萨。
注意!!还有一些披萨不会被记录,我们可以选择将所有未被记录的披萨统一放入一个虚拟的桌子,通过找到可以替换掉这个披萨的披萨来确定它所在位置。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 20;
int n, m, t[N], it[N];
bool ck[N];
vector<int> vec[N], ans;
bool cmp(vector<int> x, vector<int> y){return x[0] < y[0];}
void dfs(int x){
while(it[x] != vec[x].size() - 1){
if(x != m && vec[x][it[x] + 1] > vec[x + 1][it[x + 1]]) return ;
it[x] ++;
ans.push_back(vec[x][it[x]]);
if(x) dfs(x - 1);
}
}
signed main(){
// freopen("oven.in", "r", stdin);
// freopen("oven.out", "w", stdout);
cin >> n >> m;
for(int i = 1; i <= m; i++){
cin >> t[i];
vec[i].resize(t[i]);
for(int j = 0; j < t[i]; j++){
cin >> vec[i][j];
ck[vec[i][j]] = true;
if(j > 0){
if(vec[i][j] < vec[i][j - 1]){
cout << "NO";
return 0;
}
}
}
}
vec[0].push_back(-1);
for(int i = 0; i < n; i++){
if(!ck[i]) vec[0].push_back(i);
}
for(int i = 1; i <= m; i++) ans.push_back(vec[i][0]);
sort(vec + 1, vec + m + 1, cmp);
for(int i = 0; i <= m; i++) dfs(i);
for(int i = 0; i <= m; i++){
if(it[i] != vec[i].size() - 1){
cout << "NO";
return 0;
}
}
cout << "YES\n";
for(int x : ans) cout << x << ' ';
return 0;
}

浙公网安备 33010602011771号