Codeforces - 1199D - Welfare State - 单调栈 / 线段树

https://codeforc.es/contest/1199/problem/D

其实后来想了一下貌似是个线段树的傻逼题。

单调栈是这样思考的,每次单点修改打上一个最终修改的时间戳。每次全体修改就push进去单调栈。首先比新的全体修改的x小的(等的也)全部出栈,这样子单调栈里面就是一个递减的序列,而时间戳是递增的。

最后对于每一个有修改标记的,在时间戳上面二分找到他的下一次修改,那么这个修改绝对就是足够大的。假如没有查找成功,则说明不存在最后一次修改。(可以通过在最后入栈一个0操作来统一),没有修改标记的那就直接赋值最大的全体修改。(相当于对0进行查询)

其实也是nlogn的。常数估计更小。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

inline int read() {
    int x = 0;
    char c = getchar();
    for(; c < '0' || c > '9'; c = getchar());
    for(; c >= '0' && c <= '9'; c = getchar())
        x = (x << 3) + (x << 1) + c - '0';
    return x;
}

inline void _write(int x) {
    if(x > 9)
        _write(x / 10);
    putchar(x % 10 + '0');
}

inline void write(int x) {
    if(x < 0) {
        putchar('-');
        x = -x;
    }
    _write(x);
    putchar('\n');
}

const int MAXN=200005;

int n, q;
int a[MAXN],lc[MAXN];
int st1[MAXN],st2[MAXN],stop;

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    n = read();
    for(int i = 1; i <= n; ++i)
        a[i] = read();

    q = read();
    for(int qi = 1; qi <= q; qi++) {
        int op = read(), p, x;
        if(op == 1) {
            p = read(), x = read();
            a[p] = x;
            lc[p] = qi;
        } else {
            x = read();
            while(stop && st1[stop] <= x)
                --stop;
            st1[++stop] = x;
            st2[stop] = qi;
        }
    }
    st1[++stop] = 0;
    st2[stop] = q + 1;
    for(int i = 1; i <= n; ++i)
        a[i] = max(a[i], st1[lower_bound(st2 + 1, st2 + 1 + stop, lc[i]) - st2]);
    for(int i = 1; i <= n; ++i)
        printf("%d%c", a[i], " \n"[i == n]);
}

其实当时也在想,每次lazy更新2操作不就可以了吗?这样就直接是线段树。每次对点更新把一路上的lazy标记push下去,然后到叶子的时候把这个失效的lazy给清空了。query的时候记得要max上lazy,因为有一些叶子并没有被1操作对点更新但是lazy也确实传到这个叶子了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const int MAXM = 200000;
int a[MAXM + 5];
int lazy[(MAXM << 2) + 5];
 
inline void push_down(int o, int l, int r) {
    if(lazy[o]) {
        lazy[o << 1] = max(lazy[o << 1], lazy[o]);
        lazy[o << 1 | 1] = max(lazy[o << 1 | 1], lazy[o]);
        lazy[o] = 0;
    }
}
 
void update1(int o, int l, int r, int x, int v) {
    if(x <= l && r <= x) {
        lazy[o]=0;
        a[x] = v;
        return;
    } else {
        push_down(o, l, r);
        int m = (l + r) >> 1;
        if(x <= m)
            update1(o << 1, l, m, x, v);
        if(x >= m + 1)
            update1(o << 1 | 1, m + 1, r, x, v);
    }
}
 
void update2(int o, int l, int r, int v) {
    lazy[o] = max(lazy[o], v);
    return;
}
 
int query(int o, int l, int r, int x) {
    if(x <= l && r <= x) {
        return max(a[x], lazy[o]);
    } else {
        push_down(o, l, r);
        int m = (l + r) >> 1;
        if(x <= m)
            return query(o << 1, l, m, x);
        if(x >= m + 1)
            return query(o << 1 | 1, m + 1, r, x);
    }
}
 
int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
    //freopen("Yinku.out", "w", stdout);
#endif // Yinku
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    int q;
    scanf("%d", &q);
    for(int i = 1; i <= q; ++i) {
        int op;
        scanf("%d", &op);
        if(op == 1) {
            int x, v;
            scanf("%d%d", &x, &v);
            update1(1, 1, n, x, v);
        } else {
            int x;
            scanf("%d", &x);
            update2(1, 1, n, x);
        }
    }
    for(int i = 1; i <= n; ++i) {
        a[i] = query(1, 1, n, i);
        printf("%d%c", a[i], " \n"[i == n]);
    }
}

转载于:https://www.cnblogs.com/Yinku/p/11275436.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值