• 51nod 1471 小S的兴趣 | 分块 链表


    51nod 1471 小S的兴趣

    题面

    小S喜欢有趣的事。但是,每个人的兴趣都是独特的。小S热衷于自问自答。有一天,小S想出了一个问题。
    有一个包含n个正整数的数组a和针对这个数组的几个问题。这些问题有两种类型:

    1.  在数组下标l到r的部分上,将一个单元格循环移动到右端。即以下面方式重新分配数组上的元素。
      

    a[l], a[l+1], ..., a[r-1], a[r] → a[r], a[l], a[l+1], ..., a[r-1].
    2. 在数组下标l到r的部分上,计算有多少元素的值与k相等。

    小S很喜欢这个问题并且很快解决了它,你是否能够解决它呢?

    Input

    第一行包含整数 n (1 ≤ n ≤ 10*5) —数组元素的数量。第二行包含 n 个整数a[1], a[2], ..., a[n] (1 ≤ a[i] ≤ n)。

    第三行包含唯一的整数 q (1 ≤ q ≤ 10*5) —问题的数量。接下来的q行包含了这些询问。

    因为你需要在线回答这些问题,所以这些问题将会被编码。第一种类型的询问将会以以下形式给出: 1 Li Ri 。第二种类型的询问将会以以下形式给出: 2 Li Ri Ki 。所有输入的数字都是整数,它们满足以下条件:1 ≤ Li,Ri,Ki ≤ n.

    为解码输入的问题,你需要按以下转换操作:
    li = ((Li + lastans - 1) mod n) + 1;
    ri = ((Ri + lastans - 1) mod n) + 1;
    ki=((Ki + lastans - 1) mod n) + 1.
    lastans 是到当前询问为止最后一个第二种类型的询问的答案 (初始, lastans = 0)。如果转换后, li 比 ri 大,你需要交换这两个数字。

    Output

    对于每一个第二种类型的问题以单独一行输出答案。

    Input示例

    7
    6 6 2 7 4 2 5
    7
    1 3 6
    2 2 4 2
    2 2 4 7
    2 2 2 5
    1 2 6
    1 1 4
    2 1 7 3
    

    Output示例

    2
    1
    0
    0
    

    题解

    这道题可以用分块做!(其实我就是想练习分块才来写的这道题……)

    大约(sqrt n)(因为担心空间爆炸,我取的是400个一块)分成一块,每块内部是一个链表。对于每一块,记录某数在其中出现的次数。

    当进行修改的时候,把右端点上的元素拿下来,插到左端点前面即可。但是单纯这样做,可能会导致有的块变大、有的块变小,不利于时间复杂度的稳定,于是把区间中间的每一块最右边的元素拿下来,插到下一个块的左端点前面,这样每次修改时块的大小不会改变。

    注意可能出现修改区间的 l, r 相等的情况——对于我的代码,这里不特判的话链表操作就会GG……

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define INF 0x3f3f3f3f
    #define space putchar(' ')
    #define enter putchar('
    ')
    using namespace std;
    typedef long long ll;
    template <class T>
    bool read(T &x){
        char c;
        bool op = 0;
        while(c = getchar(), c < '0' || c > '9')
            if(c == '-') op = 1;
            else if(c == EOF) return 0;
        x = c - '0';
        while(c = getchar(), c >= '0' && c <= '9')
            x = x * 10 + c - '0';
        if(op) x = -x;
        return 1;
    }
    template <class T>
    void write(T x){
        if(x < 0) putchar('-'), x = -x;
        if(x >= 10) write(x / 10);
        putchar('0' + x % 10);
    }
    
    const int M = 400, N = 1e5+400;
    int n, q, a[N];
    int st[N/M], ed[N/M], pre[N], nxt[N];
    int blk[N], beg[N];
    // 使用链表存储每一块内部信息
    // blk是原来某一编号的元素的所在的位置
    int cnt[N/M][N], ans; // 记录每一块内每个数有多少
    
    void erase(int x){ //删除元素x
        pre[nxt[x]] = pre[x];
        nxt[pre[x]] = nxt[x];
        cnt[blk[x]][a[x]]--;
    }
    void insert(int x, int y){ //在x前面插入y(同一块)
        pre[y] = pre[x];
        nxt[y] = x;
        nxt[pre[x]] = y;
        pre[x] = y;
        blk[y] = blk[x];
        cnt[blk[y]][a[y]]++;
    }
    int find(int x){ //找到第x个元素
        int b = 1;
        while(b < blk[n] && beg[b + 1] <= x) b++;
        int ret = nxt[st[b]], pos = beg[b];
        while(pos < x) pos++, ret = nxt[ret];
        return ret;
    }
    void change(int l, int r){
        int left = find(l), right = find(r);
        int b1 = blk[left], b2 = blk[right];
        erase(right);
        insert(left, right);
        for(int b = b1; b < b2; b++){
            int x = pre[ed[b]];
            erase(x);
            insert(nxt[st[b + 1]], x);
        }
    }
    int query(int l, int r, int x){
        int left = find(l), right = find(r);
        int b1 = blk[left], b2 = blk[right];
        int ret = 0;
        if(b1 == b2){
            for(int i = left; i != nxt[right]; i = nxt[i])
                if(a[i] == x) ret++;
            return ret;
        }
        for(int pos = beg[b1 + 1] - 1, i = pre[ed[b1]]; pos >= l; pos--, i = pre[i])
            if(a[i] == x) ret++;
        for(int pos = beg[b2], i = nxt[st[b2]]; pos <= r; pos++, i = nxt[i])
            if(a[i] == x) ret++;
        for(int b = b1 + 1; b < b2; b++)
            ret += cnt[b][x];
        return ret;
    }
    int main(){
        read(n);
        for(int i = 1; i <= n; i++){
            read(a[i]);
            pre[i] = i - 1, nxt[i] = i + 1, blk[i] = (i - 1) / M + 1;
            if(i % M == 1)
                beg[blk[i]] = i, st[blk[i]] = n + blk[i], pre[i] = st[blk[i]], nxt[st[blk[i]]] = i;
            if(i % M == 0 || i == n)
                ed[blk[i]] = n + blk[i], nxt[i] = ed[blk[i]], pre[ed[blk[i]]] = i;
            cnt[blk[i]][a[i]]++;
        }
        beg[blk[n] + 1] = n + 1;
        read(q);
        while(q--){
            int op, l, r, x;
            read(op), read(l), read(r);
            l = (l + ans - 1) % n + 1;
            r = (r + ans - 1) % n + 1;
            if(l > r) swap(l, r);
            if(op == 1 && l != r)
                change(l, r);
            else if(op == 2){
                read(x);
                x = (x + ans - 1) % n + 1;
                write(ans = query(l, r, x)), enter;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    《淘宝技术这十年》读书总结
    广告:互联网公司的纽带
    广告:互联网公司的纽带
    定期存款要及时
    定期存款要及时
    Java实现蓝桥杯VIP算法训练 自行车停放
    Java实现蓝桥杯VIP算法训练 自行车停放
    Java实现蓝桥杯VIP算法训练 自行车停放
    Java实现蓝桥杯VIP算法训练 数组逆序排列
    Java实现蓝桥杯VIP算法训练 数组逆序排列
  • 原文地址:https://www.cnblogs.com/RabbitHu/p/51nod1471.html
Copyright © 2020-2023  润新知