zoukankan      html  css  js  c++  java
  • 莫队+分块 BZOJ 3809

    3809: Gty的二逼妹子序列

    Time Limit: 80 Sec  Memory Limit: 28 MB
    Submit: 1634  Solved: 482
    [Submit][Status][Discuss]

    Description

    Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题。
     
    对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数。
     
    为了方便,我们规定妹子们的美丽度全都在[1,n]中。
     
    给定一个长度为n(1<=n<=100000)的正整数序列s(1<=si<=n),对于m(1<=m<=1000000)次询问“l,r,a,b”,每次输出sl...sr中,权值∈[a,b]的权值的种类数。

    Input

    第一行包括两个整数n,m(1<=n<=100000,1<=m<=1000000),表示数列s中的元素数和询问数。
     
    第二行包括n个整数s1...sn(1<=si<=n)。
     
    接下来m行,每行包括4个整数l,r,a,b(1<=l<=r<=n,1<=a<=b<=n),意义见题目描述。
     
    保证涉及的所有数在C++的int内。
     
    保证输入合法。

    Output

    对每个询问,单独输出一行,表示sl...sr中权值∈[a,b]的权值的种类数。

    Sample Input

    10 10
    4 4 5 1 4 1 5 1 2 1
    5 9 1 2
    3 4 7 9
    4 4 2 5
    2 3 4 7
    5 10 4 4
    3 9 1 1
    1 4 5 9
    8 9 3 3
    2 2 1 6
    8 9 1 4

    Sample Output

    2
    0
    0
    2
    1
    1
    1
    0
    1
    2

    HINT

    样例的部分解释:
    5 9 1 2
    子序列为4 1 5 1 2
    在[1,2]里的权值有1,1,2,有2种,因此答案为2。
     
    3 4 7 9
    子序列为5 1
    在[7,9]里的权值有5,有1种,因此答案为1。

    4 4 2 5
    子序列为1
    没有权值在[2,5]中的,因此答案为0。
     
    2 3 4 7
    子序列为4 5
    权值在[4,7]中的有4,5,因此答案为2。
    建议使用输入/输出优化。

    = =,我之前看卿学姐的代码,思考着他的莫队+分块是怎么做的,想着想着就发现暂时不清楚怎么分块,于是打算自己尝试写写看莫队+线段树来更新,然后果然TLE,因为这样的复杂度是O(n^1.5 * logn)

    //看看会不会爆int!数组会不会少了一维!
    //取物问题一定要小心先手胜利的条件
    #include <bits/stdc++.h>
    using namespace std;
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    #pragma comment(linker,"/STACK:102400000,102400000")
    #define LL long long
    #define ALL(a) a.begin(), a.end()
    #define pb push_back
    #define mk make_pair
    #define fi first
    #define se second
    #define haha printf("haha
    ")
    const int maxn = 1e5 + 5;
    const int maxm = 1000000 + 5;
    int block, pos[maxn], sz, s[maxn];
    struct Point{
        int l, r, ca, cb, id;
    }q[maxm];
    int res[maxm];
    bool cmp(Point a, Point b){
        if(pos[a.l]==pos[b.l])
            return a.r<b.r;
        return pos[a.l]<pos[b.l];
    }
    int n, m;
    struct Sedment_tree{
        int colorcnt, colornum;
    }tree[maxn << 2];
    
    inline void push_up(int o){
        int lb = o << 1, rb = o << 1 | 1;
        tree[o].colornum = tree[lb].colornum + tree[rb].colornum;
    }
    
    void update(int p, int l, int r, int o, int val){
        if (p == l && p == r){
            tree[o].colorcnt += val;
            if (tree[o].colorcnt == 0) tree[o].colornum = 0;
            if (tree[o].colorcnt == 1) tree[o].colornum = 1;
            return ;
        }
        int mid = (l + r) / 2;
        if (p <= mid) update(p, l, mid, o << 1, val);
        if (p > mid) update(p, mid + 1, r, o << 1 | 1, val);
        push_up(o);
    }
    
    void add(int x){
        update(s[x], 1, n, 1, 1);
    }
    
    void del(int x){
        update(s[x], 1, n, 1, -1);
    }
    
    int query(int ql, int qr, int l, int r, int o){
        int ans = 0;
        if (ql <= l && qr >= r){
            ans += tree[o].colornum; return ans;
        }
        int mid = (l + r) / 2;
        if (ql <= mid) ans += query(ql, qr, l, mid, o << 1);
        if (qr > mid) ans += query(ql, qr, mid + 1, r, o << 1 | 1);
        return ans;
    }
    
    int main(){
        n = read(), m = read();
        sz = sqrt(n);
        for (int i = 1; i <= n; i++){
            s[i] = read();
            pos[i] = (i - 1) / sz;
        }
        for (int i = 1; i <= m; i++){
            q[i].l = read(), q[i].r = read(), q[i].ca = read(), q[i].cb = read();
            q[i].id = i;
        }
        sort(q + 1, q + 1 + m, cmp);
        int L = 1, R = 0;
        for (int i = 1; i <= m; i++){
            while (L > q[i].l){
                L--; add(L);
            }
            while (R < q[i].r){
                R++; add(R);
            }
            while (L < q[i].l){
                L++; del(L - 1);
            }
            while (R > q[i].r){
                R--; del(R + 1);
            }
            res[q[i].id] = query(q[i].ca, q[i].cb, 1, n, 1);
        }
        for (int i = 1; i <= m; i++){
            printf("%d
    ", res[i]);
        }
        return 0;
    }
    View Code

    (好吧,隔了好久重新看了一下这题,发现是自己傻逼了,应该是可以用线段树的....吧?,具体怎么用的话就是修改一下add和del函数就好了,大致修改的和分块差不多?不过好像最坏情况还是会TLE唉) 

    因此改为莫队+分块吧

    然后思考了一下,我们对于询问区间进行莫队离线操作即可。然后对于修改操作,我们就利用分块来控制颜色区间即可。

    //看看会不会爆int!数组会不会少了一维!
    //取物问题一定要小心先手胜利的条件
    #include <bits/stdc++.h>
    using namespace std;
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    #pragma comment(linker,"/STACK:102400000,102400000")
    #define LL long long
    #define ALL(a) a.begin(), a.end()
    #define pb push_back
    #define mk make_pair
    #define fi first
    #define se second
    #define haha printf("haha
    ")
    const int maxn = 1e5 + 5;
    const int maxm = 1000000 + 5;
    int block, pos[maxn], sz, belong[maxn], num, l[maxn], r[maxn], cnt[maxn];
    ///pos是莫队算法,表示目前这个询问区间是哪个块的
    ///belong是分块,表示目前这个颜色是属于哪个块的
    int s[maxn];
    struct Point{
        int l, r, ca, cb, id;
    }q[maxm];
    int res[maxm], color[maxn];
    bool cmp(Point a, Point b){
        if(pos[a.l]==pos[b.l])
            return a.r<b.r;
        return pos[a.l]<pos[b.l];
    }
    int n, m;
    
    void build(){
        block = sqrt(n); num = n / block;
        if (n % block) num++;
        for (int i = 1; i <= num; i++){
            l[i] = (i - 1) * block + 1; r[i] = i * block;
        }
        r[num] = n;
        for (int i = 1; i <= n; i++){
            belong[i] = (i - 1) / block + 1;
        }
    }
    
    void add(int x){
        color[x]++;
        if(color[x] == 1) cnt[belong[x]]++;
    }
    
    void del(int x){
        color[x]--;
        if(color[x] == 0) cnt[belong[x]]--;
    }
    
    int query(int x, int y){
        int ans = 0;
        if (belong[x] == belong[y]){
            for (int i = x; i <= y; i++) if (color[i]) ans++;
            return ans;
        }
        for (int i = x; i <= r[belong[x]]; i++){
            if (color[i]) ans++;
        }
        for (int i = l[belong[y]]; i <= y; i++){
            if (color[i]) ans++;
        }
        for (int i = belong[x] + 1; i < belong[y]; i++)
            ans += cnt[i];
        return ans;
    }
    
    int main(){
        n = read(), m = read();
        sz = sqrt(n);
        for (int i = 1; i <= n; i++){
            s[i] = read();
            pos[i] = (i - 1) / sz;
        }
        for (int i = 1; i <= m; i++){
            q[i].l = read(), q[i].r = read(), q[i].ca = read(), q[i].cb = read();
            q[i].id = i;
        }
        sort(q + 1, q + 1 + m, cmp);
        int L = 1, R = 0;
        build();
        for (int i = 1; i <= m; i++){
            while (L > q[i].l){
                L--; add(s[L]);
            }
            while (R < q[i].r){
                R++; add(s[R]);
            }
            while (L < q[i].l){
                L++; del(s[L - 1]);
            }
            while (R > q[i].r){
                R--; del(s[R + 1]);
            }
            res[q[i].id] = query(q[i].ca, q[i].cb);
        }
        for (int i = 1; i <= m; i++){
            printf("%d
    ", res[i]);
        }
        return 0;
    }
    View Code

    关键:深刻了解莫队算法和分块

  • 相关阅读:
    《动手能力强与技术水平低》(2009/12/14)
    《为什么程序员被喻为“IT农民工”》(2009/12/12)
    对于关键字Ref和Out的理解
    2008世界500强排名(1100位)
    Javascript取select的选中值和文本
    使用 FreeNAS 下載 eMule
    程序员35岁后的三条出路
    二维条码 QR Code
    笔记本CPU性能排行榜Comparison of Mobile Processors (CPU Benchmarks)
    如何在C#中播放AVI短片并使背景透明
  • 原文地址:https://www.cnblogs.com/heimao5027/p/6529912.html
Copyright © 2011-2022 走看看