题解:P10300 [THUWC 2020] 工资分配
题目传送门
题意简述:给定一个长为
k 的初始数组\lbrace a_i \rbrace ,依次进行n 次操作(p_t, \lbrace b_{t,i} \rbrace) :如果当前的a_{p_t} 小于b_{t,p_t} ,则将整个\lbrace a_i \rbrace 替换为\lbrace b_{t,i} \rbrace ,否则维持\lbrace a_i \rbrace 不变。输出n 次操作后的\lbrace a_i \rbrace 。q 次询问,每次询问会给出\lbrace a_i \rbrace 的不同初始值。n,q \le 10^5, k \le 20 。
不难想到暴力模拟上面的过程,时间复杂度最坏
考虑优化。在上面的做法中,如果发生了 break; 就可以通过了,跑得还很快。好像目前为止其他题解都是这个做法?
继续优化上面的做法,发现上一做法的瓶颈为暴力向后找首个替换。我们可以维护
// 当前需要找到 a[k] 第一次被替换的位置,b[n][k] 是操作序列,v[k] 是 k 个单调栈,用 vector 实现
inline int ask(int *a){
int pos=n+1;
for(int i=1;i<=k;i++){ // 遍历每个位置
int l=0,r=v[i].size();
while(l<r){
const int mid=(l+r)>>1;
b[v[i][mid]][i]<=a[i]?r=mid:l=mid+1;
} // 二分找到第一个小于等于某数的数的位置 l,l-1 就是最后一个大于该数的数
if(l)pos=min(pos,v[i][l-1]); // 取所有结果的最小值
}
return pos;
}
// 对 t=i 维护单调栈,栈顶到栈底严格递增
while(v[p[i]].size() && b[v[p[i]].back()][p[i]]<=b[i][p[i]])
v[p[i]].pop_back();
v[p[i]].emplace_back(i);
询问时同理。总时间复杂度