2021 牛客暑期多校训赛2 K - Stack (模拟+拓扑排序)
题意
Stk is an empty stack
for i = 1 to n :
while ( Stk is not empty ) and ( Stk's top > a[i] ) :
pop Stk
push a[i]
b[i]=Stk's size
题目给了一段伪码,一看就知道是单调栈
将 a a a数组依次入栈,每次入栈时记录单调栈内元素个数
然后题目给你 b b b数组中的某些元素,让你反推出 a a a数组
解题思路
考虑模拟这个单调栈进栈的过程,并将进栈过程中的栈大小拟合 b b b数组,再反推出 a a a各元素之间的大小关系
分析
建一个栈 s t k stk stk,使它满足单调栈的性质和入栈过程:即栈内元素单调上升,入栈时需要确保栈顶元素小于待入栈元素,否则不断从栈顶弹出元素直至栈顶元素小于待入栈元素为止
但需要注意的是,这里我们向栈中加入的是数组下标,而判断条件则是根据它对应的值,既假如我们待入栈的元素为 u u u,栈顶元素为 v v v,则根据我们规定的入栈规则应有, a u > a v a_{u} > a_{v} au>av,若当前 v v v不满足条件,则不断弹出栈顶元素直至满足条件为止
而现在我们并不知道 a a a数组的具体值,就需要根据 b b b数组去构造,考虑通过 s t k stk stk模拟 a i a_i ai入栈(注意只是模拟,我们实际上入栈的是 i i i),并根据 b i b_i bi的值调整 s t k stk stk大小,则我们可以有如下构造策略:
设 b b b数组初值为0, b p i = x i b_{p_i} = x_i bpi=xi
设 b i ′ b^{'}_i bi′表示当前处理到位置 i i i时,我们模拟的单调栈 s t k stk stk的大小
遍历数组 b b b,对于当前 b i b_i bi,设当前待入栈元素为 u u u,当:
- b i = = 0 b_i==0 bi==0,采取贪心策略,使得 b i ′ = b i − 1 ′ + 1 b^{'}_i = b^{'}_{i-1} + 1 bi′=bi−1′+1,这样可以保证 b i ′ b^{'}_i bi′尽量大,因为 b i ′ b^{'}_i bi′可以减小而不能增大,采取贪心策略可以保证后面的情况在有解的情况下尽量可解,设当前顶元素为 v v v,为了满足 b i ′ = b i − 1 ′ + 1 b^{'}_i = b^{'}_{i-1} + 1 bi′=bi−1′+1,则应有 a u > a v a_u > a_v au>av,这里我们只要递增
- b i − 1 ′ + 1 < b i b^{'}_{i-1} + 1<b_i bi−1′+1<bi,无解,因为前面采取了贪心策略,因此不存在一种 b i − 1 ′ ′ > b i − 1 ′ b^{''}_{i-1}>b^{'}_{i-1} bi−1′′>bi−1′使得 b i − 1 ′ ′ + 1 > = b i b^{''}_{i-1} + 1 >= b_i bi−1′′+1>=bi
- b i − 1 ′ + 1 > b i b^{'}_{i-1}+1>b_i bi−1′+1>bi,弹出栈顶元素直至 b i − 1 ′ + 1 = = b i b^{'}_{i-1}+1==b_i bi−1′+1==bi,设弹出元素的集合为 V = { v 1 , v 2 , . . . , v m } V=\{v_1,v_2,...,v_m\} V={v1,v2,...,vm},则根据单调栈 s t k stk stk所规定的性质可得: ∀ v i ∈ V , a v i > a u \forall v_i\in V,a_{v_i}>a_u ∀vi∈V,avi>au
- b i − 1 ′ + 1 = = b i b^{'}_{i-1}+1==b_i bi−1′+1==bi,别无选择,只能使得 b i ′ = b i − 1 ′ + 1 b^{'}_{i}=b^{'}_{i-1}+1 bi′=bi−1′+1,设当前栈顶元素为 v v v,则有 a u > a v a_u>a_v au>av
(ps:由于我们单调栈内保存的是下标,所以每次都令待入栈元素 u = = i u==i u==i,在遍历数组 b b b时递增处理每一个位置即可)
这样一来我们不难得到系列的大小关系,我们考虑将每一组大小关系转化为拓扑关系,即对于每一对 u , v ∈ { 1 , 2 , . . . n } u, v \in \{1,2,...n\} u,v∈{1,2,...n}, a u < a v a_u < a_v au<av,连一条从 u u u到 v v v的边,最后对整张图做拓扑排序。设 u , v u,v u,v的拓扑排序值分别为 t u , t v t_u,t_v tu,tv,根据拓扑排序的性质有 t u < t v t_u<t_v tu<tv,分别与每一组 a u < a v a_u<a_v au<av相对应,因此我们可以将拓扑排序 t 1 , t 2 , . . . , t n t_1,t_2,...,t_n t1,t2,...,tn作为 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an的一组合法构造
AC代码
#include <bits/stdc++.h>
const int N = 1e6 + 5, M = 1e7 + 5;
int head[N], d[N], nex[M], to[M], idx;
void edge_to(int u, int v) {
nex[++idx] = head[u];
head[u] = idx;
to[idx] = v;
++d[v];
}
int b[N], a[N];
int n, k;
void topsort() {
std::queue<int> que;
for(int i = 1; i <= n; ++i)
if(!d[i])
que.push(i);
int ord = 1;
while(!que.empty()) {
int u = que.front(); que.pop();
a[u] = ord++;
for(int i = head[u]; i; i = nex[i]) {
int v = to[i];
--d[v];
if(!d[v]) que.push(v);
}
}
}
int main() {
scanf("%d%d", &n, &k);
for(int i = 0, p, x; i < k; ++i) {
scanf("%d%d", &p, &x);
b[p] = x;
}
std::stack<int> stk;
for(int i = 1; i <= n; ++i) {
if(stk.size() + 1 < b[i]) {
printf("-1");
exit(0);
}
while(b[i] && stk.size() + 1 > b[i]) {
int t = stk.top(); stk.pop();
edge_to(i, t);
}
if(stk.size())
edge_to(stk.top(), i);
stk.push(i);
}
topsort();
for(int i = 1; i <= n; ++i)
printf("%d ", a[i]);
return 0;
}