• HDU 4638 Group 【树状数组,分块乱搞(莫队算法?)】


    根据题目意思,很容易得出,一个区间里面连续的段数即为最少的group数。

    题解上面给的是用树状数组维护的。

    询问一个区间的时候,可以一个一个的向里面添加,只需要判断a[i]-1 和 a[i]+1是否已经添加在内,如果两个都在,则总段数减1,如果两个都不在,总段数加1,其他情况总段数不变了。这里有一个需要深入理解的就是其实无论是按顺序添加还是随便添加,统计结果是不变的,但是要看怎么维护了。

    每加入一个点,都会有一个改变量v[i],那么此时总段数就是sum{ v[i] } (1 <= i <= x) ,因此用树状数组维护前缀和即可。


    树状数组实现:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define N 101000
    bool vis[N];
    int n, s[N], m, ans[N], a[N], p[N];
    struct node {
        int l, r, id;
        bool operator<(const node &x) const {
            return r > x.r;
        }
    } q[N];
    inline int lowbit(int x) {
        return x&(-x);
    }
    void add(int x, int v) {
        while (x <= n) {
            s[x] += v;
            x += lowbit(x);
        }
    }
    int sum(int x) {
        int ret = 0;
        while (x) {
            ret += s[x];
            x -= lowbit(x);
        }
        return ret;
    }
    int main() {
        int T;
        scanf("%d", &T);
        while (T--) {
            scanf("%d%d", &n, &m);
            for (int i=1; i<=n; i++) {
                scanf("%d", &a[i]);
                p[a[i]] = i;
            }
            memset(s, 0, sizeof(s));
            memset(vis, false, sizeof(vis));
            for (int i=n; i>0; i--) {
                if (vis[a[i]-1] && vis[a[i]+1]) add(i, -1);
                if (!vis[a[i]-1] && !vis[a[i]+1]) add(i, 1);
                vis[a[i]] = true;
            }
    
            for (int i=0; i<m; i++) {
                scanf("%d%d", &q[i].l, &q[i].r);
                q[i].id = i;
            }
    
            sort(q, q+m);
            int j = n;
            for (int i=0; i<m; i++) {
                while (j > q[i].r) {
                    if (a[j]>1 && p[a[j]-1] < j) add(p[a[j]-1], 1);
                    if (a[j]<n && p[a[j]+1] < j) add(p[a[j]+1], 1);
                    j--;
                }
                ans[q[i].id] = sum(q[i].r) - sum(q[i].l-1);
            }
    
            for (int i=0; i<m; i++) printf("%d
    ", ans[i]);
        }
    
        return 0;
    }
    



    比赛时候,我用的分块的思想,想到复杂度是O(n*sqrt(n))的,可能有点悬,结果TLE了,以为真的是卡时间了,其实是自己的代码写残了(一个小细节害死人啊),调试了很长时间,直接用原题的大数据debug是要命啊。总算找到bug了。其实也是可以过的么……呵呵……数据水?

    种解法和CF 86D Powerful array 的解法是一样的。


    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    #define N 100100
    bool vis[N];
    int a[N], n, m, all, L, R, ans[N];
    
    struct node {
        int l, r, b, id;
        bool operator<(const node &x)const{
            if (b == x.b) return r < x.r;
            return b < x.b;
        }
    } q[N];
    
    void query(int x, int y, int flag) {
        if (flag != 0) {
            for (int i=x; i<L; i++) {
                vis[a[i]] = true;
                if (vis[a[i]-1] && vis[a[i]+1]) all--;
                else if (!vis[a[i]-1] && !vis[a[i]+1]) all++;
            }
            for (int i=R+1; i<=y; i++) {
                vis[a[i]] = true;
                if (vis[a[i]-1] && vis[a[i]+1]) all--;
                else if (!vis[a[i]-1] && !vis[a[i]+1]) all++;
            }
            for (int i=L; i<x; i++) {
                vis[a[i]] = false;
                if (vis[a[i]-1] && vis[a[i]+1]) all++;
                else if (!vis[a[i]-1] && !vis[a[i]+1]) all--;
            }
            for (int i=y+1; i<=R; i++) {
                vis[a[i]] = false;
                if (vis[a[i]-1] && vis[a[i]+1]) all++;
                else if (!vis[a[i]-1] && !vis[a[i]+1]) all--;
            }
        } else {
            all = 0;
            for (int i=x; i<=y; i++) {
                vis[a[i]] = true;
                if (vis[a[i]-1] && vis[a[i]+1]) all--;
                else if (!vis[a[i]-1] && !vis[a[i]+1]) all++;
            }
        }
        L = x, R = y;
    }
    int main() {
        int T;
        scanf("%d", &T);
    
        while (T--) {
            scanf("%d%d", &n, &m);
            int block_size = sqrt(n);
    
            for (int i=1; i<=n; i++)  scanf("%d", &a[i]);
            for (int i=0; i<m; i++) {
                scanf("%d%d", &q[i].l, &q[i].r);
                q[i].b = q[i].l / block_size;
                q[i].id = i;
            }
            sort(q, q+m);
            memset(vis, false, sizeof(vis));
    
            for (int i=0; i<m; i++) {
                query(q[i].l, q[i].r, i);
                ans[q[i].id] = all;
            }
            for (int i=0; i<m; i++)  printf("%d
    ", ans[i]);
        }
    
        return 0;
    }
    



  • 相关阅读:
    码农的半衰期只有15年?
    微软面试100题2010年版全部答案集锦(转自July)
    大量url,如何去重
    后缀树求最长子字符串
    转 STL hash_map & map
    有n 个长为m+1 的字符串,求前后m个字符匹配所能形成的最长字符串链:利用弗洛伊德算法求最长路径
    获取本机地址信息,遇到小问题...有待解决
    HDOJ 1006
    归并排序
    插入排序的简单实现
  • 原文地址:https://www.cnblogs.com/riskyer/p/3235499.html
Copyright © 2020-2023  润新知