• Codeforces 650 D. Zip-line


    $ >Codeforces space 650 D. Zip-line<$

    题目大意 :
    有一个长度为 (n) 的序列 (h)(m) 次询问,每一次询问求如果把序列中第 (x) 元素变成 (y) 后的 (lis) 长度

    (1 leq n, m leq 4 imes 10^5)

    解题思路 :

    考虑答案的形态由两部分组成,一部分是包含 (x)(lis) ,一部分是不包含 (x)(lis)

    前者显然可以维护左右两个 (dp) 值然后主席树数一下点,难度在于后者。

    对于第二部分,如果 (x) 是之前所有 (lis) 共有的点,那么第二部分的答案就是 (lis-1) ,否则是 (lis)

    考虑怎么判断一个点是否是所有 (lis) 共有,下面先给出方法:

    统计每一个点在 (lis) 中出现的位置情况,设 (l[i]) 表示从左到右以 (i) 结尾的 (lis) 长度,(r[i]) 表示从右到左以 (i) 结尾的 (lis) 长度,如果 (l[i]+r[i]-1=lis),那么 (i)(lis) 中的出现位置就是 (l[i]),记为 (pos[i])

    如果说点 (x)(lis) 中的出现位置 (z) 只有 (x) 满足 (pos[x] =z) ,那么显然 (x) 是不能被替代的,其是所有 (lis) 共有的点。不然的话必然存在一种方案不经过 (x) 到另外一个满足 (pos[i] = z) 的点 (i) ,因为 (x) 在任何方案下不能存在于两个位置,这样的话 (lis) 长度就会更长,有矛盾。

    所以只需要统计一下每一个 (i) 是不是在所有的 (lis) 中出现即可,总复杂度 (O((n+m)logn))

    某位神仙表示直接上分治 (O(nlog^2n)) 就过了


    /*program by mangoyang*/
    #pragma GCC optimize("Ofast","inline","-ffast-math")
    #pragma GCC target("avx,sse2,sse3,sse4,mmx")
    #include<bits/stdc++.h>
    #define inf (0x7f7f7f7f)
    #define Max(a, b) ((a) > (b) ? (a) : (b))
    #define Min(a, b) ((a) < (b) ? (a) : (b))
    typedef long long ll;
    using namespace std;
    template <class T>
    inline void read(T &x){
        int f = 0, ch = 0; x = 0;
        for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
        for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
        if(f) x = -x;
    }
    const int N = 400005;
    int h[N], L[N], R[N], g[N], pos[N], tot[N], lis, n, m;
     
    struct SegmentTree{
        int s[N*25], lc[N*25], rc[N*25], rt[N], size;
        inline SegmentTree(){ memset(s, 127, sizeof(s)); }
        inline void ins(int &u, int pr, int l, int r, int pos, int x){
            u = ++size, lc[u] = lc[pr], rc[u] = rc[pr];
            if(l == r) return (void)(s[u] = min(s[u], x));
            int mid = l + r >> 1;
            if(pos <= mid) ins(lc[u], lc[pr], l, mid, pos, x);
            else ins(rc[u], rc[pr], mid + 1, r, pos, x);
            s[u] = min(s[lc[u]], s[rc[u]]);
        }
        inline int query(int u, int l, int r, int x){
            if(l == r) return l;
            int mid = l + r >> 1;
            if(s[rc[u]] < x) return query(rc[u], mid + 1, r, x);
            if(s[lc[u]] < x) return query(lc[u], l, mid, x);
            return 0;
        }
    }S1, S2;
    int main(){
        read(n), read(m);
        for(int i = 1; i <= n; i++) read(h[i]);
        for(int i = 1; i <= n; i++) g[i] = inf;
        for(int i = 1; i <= n; i++){
            L[i] = lower_bound(g, g + n, h[i]) - g;
            g[L[i]] = min(g[L[i]], h[i]), lis = Max(lis, L[i]);
        }
        for(int i = 0; i <= n; i++) g[i] = 0; g[0] = -inf;
        for(int i = n; i >= 1; i--){
            R[i] = lower_bound(g, g + n, -h[i]) - g;
            g[R[i]] = min(g[R[i]], -h[i]);
        }
        for(int i = 1; i <= n; i++)
            if(L[i] + R[i] - 1 == lis) pos[i] = L[i], tot[pos[i]]++;
        for(int i = 1; i <= n; i++)
            S1.ins(S1.rt[i], S1.rt[i-1], 1, n, L[i], h[i]);
        for(int i = n; i >= 1; i--)
            S2.ins(S2.rt[i], S2.rt[i+1], 1, n, R[i], -h[i]);
        for(int i = 1, x, y; i <= m; i++){
            read(x), read(y); int res = 0;
            if(x > 1) res += S1.query(S1.rt[x-1], 1, n, y);
            if(x < n) res += S2.query(S2.rt[x+1], 1, n, -y);
            printf("%d
    ", max(res + 1, (pos[x] && (tot[pos[x]] == 1)) ? lis - 1 : lis));
        }
        return 0;
    }
    
  • 相关阅读:
    POJ 2456 Aggressive cows
    POJ 1064 Cable master
    POJ 3723 Conscription
    左偏树
    tarjan模板
    [bzoj5017][Snoi2017]炸弹 tarjan缩点+线段树优化建图+拓扑
    [BZOJ4520][Cqoi2016]K远点对 kd-tree 优先队列
    [bzoj3218]a + b Problem 网络流+主席树优化建图
    #6034. 「雅礼集训 2017 Day2」线段游戏 李超树
    【UOJ UNR #1】火车管理 可持久化线段树
  • 原文地址:https://www.cnblogs.com/mangoyang/p/9831705.html
Copyright © 2020-2023  润新知