2021 牛客暑期多校训赛2 K - Stack (模拟+拓扑排序)

2021 牛客暑期多校训赛2 K - Stack (模拟+拓扑排序)

题目链接: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=bi1+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=bi1+1,则应有 a u > a v a_u > a_v au>av,这里我们只要递增
  • b i − 1 ′ + 1 < b i b^{'}_{i-1} + 1<b_i bi1+1<bi,无解,因为前面采取了贪心策略,因此不存在一种 b i − 1 ′ ′ > b i − 1 ′ b^{''}_{i-1}>b^{'}_{i-1} bi1>bi1使得 b i − 1 ′ ′ + 1 > = b i b^{''}_{i-1} + 1 >= b_i bi1+1>=bi
  • b i − 1 ′ + 1 > b i b^{'}_{i-1}+1>b_i bi1+1>bi,弹出栈顶元素直至 b i − 1 ′ + 1 = = b i b^{'}_{i-1}+1==b_i bi1+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 viV,avi>au
  • b i − 1 ′ + 1 = = b i b^{'}_{i-1}+1==b_i bi1+1==bi,别无选择,只能使得 b i ′ = b i − 1 ′ + 1 b^{'}_{i}=b^{'}_{i-1}+1 bi=bi1+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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值