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);
    }
}
原文地址:https://www.cnblogs.com/litstrong/p/3282179.html