• Frequent values && Ping pong


    Frequent values

    题意是不同颜色区间首尾相接,询问一个区间内同色区间的最长长度。

    网上流行的做法,包括翻出来之前POJ的代码也是RMQ做法,对于序列上的每个数,记录该数向左和向右延续的最远位置,那么对于一个查询Q(L, R),它的答案就分成了三种情况right(L) - L,R - left(R)以及Q(L+right(L),R-left(R))。

    这里给出一个线段树做法,在线段树的节点上维护3个量:l_value, r_value, value分别表示以左端点为起始点,以右端点为起始点以及该区间内的最大的连续长度,更新时通过两个子区间相接的地方是否相同分不同的情况进行讨论。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 100005;
    
    class SegNode {
    public:
        int L, R;
        int l_value, r_value, value;
        int is_same;
    } node[4 * MAXN];
    
    
    int num[MAXN];
    
    class SegTree {
    public:
        void log(int idx) {
            printf("%d: ", idx);
            for (int i = node[idx].L; i <= node[idx].R; i++) 
                printf("%d ", num[i]);
            printf("(%d %d %d %d)
    ", node[idx].l_value, node[idx].r_value, node[idx].value, node[idx].is_same);
        }
        void build(int root, int L, int R) {
    
            node[root].L = L;
            node[root].R = R;
    
            if (L == R) {
                // leaf
                node[root].l_value = 1;
                node[root].r_value = 1;
                node[root].value = 1;
                node[root].is_same = 1;
            } else {
                // non leaf
                int M = (L + R) / 2;
                if (L <= M) {
                    build(2 * root, L, M);
                }
                if (M + 1 <= R) {
                    build(2 * root + 1, M + 1, R);
                }
                if (num[node[2 * root].R] == num[node[2 * root + 1].L]) {
                    node[root].l_value = node[2 * root].l_value + node[2 * root].is_same * node[2 * root + 1].l_value;
                    node[root].r_value = node[2 * root + 1].r_value + node[2 * root + 1].is_same * node[2 * root].r_value;
                    node[root].value = max(max(node[2 * root].value, node[2 * root + 1].value), node[2 * root].r_value + node[2 * root + 1].l_value);
                    node[root].is_same = node[2 * root].is_same & node[2 * root + 1].is_same;
                } else {
                    node[root].l_value = node[2 * root].l_value;
                    node[root].r_value = node[2 * root + 1].r_value;
                    node[root].value = max(node[2 * root].value, node[2 * root + 1].value);
                    node[root].is_same = 0;
                }
                //log(root);
            }
        }
        int query(int root, int L, int R, int k) {
            if (L <= node[root].L && R >= node[root].R) {
                if (k == 0) return node[root].value;
                else if (k == 1) return node[root].l_value;
                else return node[root].r_value;
            }        
    
            if (L > node[root].R || R < node[root].L) {
                return 0;
            }
    
            int M = (node[root].L + node[root].R) / 2;
            if (R <= M) {
                return query(2 * root, L, R, k);
            } else if (L > M) {
                return query(2 * root + 1, L, R, k);
            } else {
                if (num[node[2 * root].R] == num[node[2 * root + 1].L]) {
                    if (k == 0) {
                        int res = 0;
                        res = max(query(2 * root, L, R, 0), query(2 * root + 1, L, R, 0));
                        res = max(res, query(2 * root, L, R, 2) + query(2 * root + 1, L, R, 1));
                        return res;
                    } else if (k == 1) {
                        int res = query(2 * root, L, R, 1);
                        if (node[2 * root].is_same) res += query(2 * root + 1, L, R, 1);
                        return res;
                    } else {
                        int res = query(2 * root + 1, L, R, 2);
                        if (node[2 * root + 1].is_same) res += query(2 * root, L, R, 2);
                        return res;
                    }
                } else {
                    if (k == 0) {
                        return max(query(2 * root, L, R, 0), query(2 * root + 1, L, R, 0));
                    } else if (k == 1) {
                        return query(2 * root, L, R, 1);
                    } else {
                        return query(2 * root + 1, L, R, 2); 
                    }
                }  
            }
        }
    } tree;
    
    int main() {
        int n, q;
        while (scanf("%d%d", &n, &q) && n) {
            for (int i = 1; i <= n; i++)
                scanf("%d", &num[i]);
            tree.build(1, 1, n);
            while (q--) {
                int l, r;
                scanf("%d%d", &l, &r);
                printf("%d
    ", tree.query(1, l, r, 0));
            }
        }
    }

    由这题想到了最大连续子段和这个问题,常见的解法是动态规划解法,算法课上讲了一个分治的解法,将整段分成左右两半,然后在中间点处向左和向右遍历,寻找最长的连续段,算法复杂度分析T(n)=2T(n/2)+O(n),因此复杂度是O(nlgn)。然而可以使用上面的思路进行维护,维护一个以左端点为起点的最长连续子段,该子段记作left(root),讲root分成L,R,那么left(root)=max{left(L), sum(L)+left(R)},这样查询中点mid的最优值就可以用right(L)+left(R)来替代了,复杂度为O(1),最后也就可以做到复杂度为O(nlgn)的最大子段和的分治算法了。

    Ping pong

    题意是各个序列,统计这样的三元组(a,b,c),满足条件idx(a)<idx(b)<idx(c),且a<b<c或者a>b>c的数量。

    做法是用树状数组统计出给定一个索引i,i左侧比a[i]小的数量,以及右侧比a[i]小的数量,用左侧比a[i]小的数量乘上右侧比a[i]大的数量,以及左侧比a[i]大的数量乘上右侧比a[i]小的数量。

    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    const int MAXA = 100005;
    const int MAXN = 20005;
    
    int c[MAXA];
    int a[MAXN];
    int left[MAXN], right[MAXN];
    
    int lowbit(int x) {
        return x & (-x);
    }
    
    void insert(int i, int x) {
        while (i < MAXA) {
            c[i] += x;
            i += lowbit(i);
        }
    }
    
    int query(int i) {
        int res = 0;
        while (i > 0) {
            res += c[i];
            i -= lowbit(i);
        }
        return res;
    }
    
    int main() {
        int T;
        scanf("%d", &T);
        while (T--) {
            int n;
            scanf("%d", &n);
            for (int i = 0; i < n; i++)
                scanf("%d", &a[i]);
            memset(c, 0, sizeof(c));
            for (int i = 0; i < n; i++) {
                left[i] = query(a[i]);
                insert(a[i], 1);
            }    
            memset(c, 0, sizeof(c));
            for (int i = n - 1; i >= 0; i--) {
                right[i] = query(a[i]);
                insert(a[i], 1);
            }
            long long ans = 0;
            for (int i = 0; i < n; i++) { 
                ans += (long long)left[i] * (n - 1 - i - right[i]);
                ans += (i - left[i]) * (long long)right[i];
            }
            printf("%lld
    ", ans);
        }
    }
  • 相关阅读:
    Oracle基础 07 参数文件 pfile/spfile
    Oracle基础 06 控制文件 controlfile
    Oracle基础 05 联机日志 redolog
    Oracle基础 04 归档日志 archivelog
    Oracle基础 02 临时表空间 temp
    Oracle基础 03 回滚表空间 undo
    Oracle基础 01 表空间 tablespace
    PL/SQL Developer 连接 Oracle
    Windows下卸载Oracle
    Vue:基础语法
  • 原文地址:https://www.cnblogs.com/litstrong/p/3282179.html
Copyright © 2020-2023  润新知