CF1945G Cook and Porridge 题解

· · 题解

你需要的前置知识仅有结构体、重载运算符和 STL 的基本使用。 考虑模拟题目中的操作,建立两个队列 $Q_1$ 与 $Q_2$(前者是普通队列,使用 `std::vector` 模拟,存储开始时的所有学生;后者是优先队列,使用 `std::priority_queue` 模拟,表示后来插队进来的学生)。定义一个“学生”结构体,存储该学生的优先级 $k$、单次吃粥时间 $s$ 与进入 $Q_2$ 的时间 $t$(如果有),按照题目以 $k$ 为第一关键字(递减),$t$ 为第二关键字(递增),$s$ 为第三关键字(递增)。 可以使用一个指针扫 $Q_1$,如果该指针扫到尾了就代表所有学生都领到粥了。按照时间 $0,1,\ldots,D$ 依次模拟,考虑当前领到粥的是 $Q_1$ 中的人还是 $Q_2$ 中的人:如果 $Q_2$ 为空或者 $Q_2$ 中最大的 $k$ 不大于 $Q_1$ 中最大的 $k$,那么 $Q_2$ 没有人能直接插队到队首,$Q_1$ 的队头可以领一份粥;反之亦然。 接着考虑领到粥的人吃完粥的时间,在那个时间把他扔进 $Q_2$ 里面。 因为 $Q_1$ 是按顺序领粥的,所以求 $Q_1$ 中剩余的最大的 $k$ 只需维护原序列的 $k$ 的后缀最大值即可。 注意判断无解情况。 放代码: ```cpp #include<bits/stdc++.h> using namespace std; struct Student{ int k,s,t; Student(){t=0;} Student(int k_,int s_,int t_){k=k_,s=s_,t=t_;} bool operator <(const Student &x)const{ return k==x.k?t==x.t?s>x.s:t>x.t:k<x.k; } // 注意 std::priority_queue 的排序规则是反着的 }; int main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ int n,d,p=0,w=-1; cin>>n>>d; vector<Student> Q1(n); for(auto &[k,s,t]:Q1)cin>>k>>s; vector<int> s(n); for(int i=n-1;~i;i--) s[i]=max(i==n-1?0:s[i+1],Q1[i].k); // 维护后缀最大值 priority_queue<Student> Q2; vector<vector<Student> > v(d); for(int i=0;i<d&&w<0;i++){ if(Q2.empty()||Q2.top().k<=s[p]){ if(int l=i+Q1[p].s;l<d) v[l].emplace_back(Q1[p]); if(++p==n)w=i+1; // 所有人都吃了 } // 从 Q1 队头拿人 else{ if(int l=i+Q2.top().s;l<d) v[l].emplace_back(Q2.top()); Q2.pop(); } // 从 Q2 队头拿人 for(auto [k,s,t]:v[i]) Q2.emplace(k,s,i); // 把吃完粥的人扔进 Q2 } cout<<w<<'\n'; } return 0; } ```