zoukankan      html  css  js  c++  java
  • SPOJ TEMPLEQ

    题意:

    有N个队伍(1 <= N <= 100,000),每个队伍开始有ai个人[0 <= ai<= 100,000,000],有Q个操作[0<=Q<= 500,000]

    操作分为三种,1 A:表示在第A个队列加一个人。 2 X:表示求长度大于等于X队列数量。3 Y:表示所有长度大于等于Y的队列减去一个人。

    题解:

    把各个队列按长度排序

    用差分数列来维护这个数组,这样求每个队列的长度就是求前缀和。每次求长度的复杂度是lgn,因为队列是按长度排序的,所以可以通过二分查找到某个长度在队列中的位置,复杂度为lgn*lgn。

    两个数组sa[i]记录每个按长度排序后的第i个队列原来的位置。 rk[i]记录在位置i的队列按长度排序的位置。

    对于在第i个队列加一个人,求出第i个队列的长度len,在所有长度为len的队列的最后一个加一,这样操作是为了不改变队列的顺序,然后是需要交换下i的位置和队列中长度为len的最后一个位置就好了。

    AC代码(950MS):

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 100005;
    struct Node {
        int pos;
        int len;
        bool operator < (Node x) const {
            return len < x.len;
        }
    } a[N];
    int sa[N], rk[N], bit[N];
    int n, q;
    
    int lowbit(int x) { return x & -x; }
    
    void add(int pos, int val)
    {
        while (pos <= n) {
            bit[pos] += val;
            pos += lowbit(pos);
        }
    }
    
    int sum(int pos)
    {
        int res = 0;
        while (pos) {
            res += bit[pos];
            pos -= lowbit(pos);
        }
        return res;
    }
    
    int lb(int x)
    {                                           // 找第一个大于等于x的数
        int l = 1, r = n + 1, m;
        while (l < r) {
            m = (l + r) >> 1;
            if (sum(m) < x) l = m + 1;
            else r = m;
        }
        return r;
    }
    
    int main()
    {
        while (~scanf("%d%d", &n, &q)) {
            for (int i = 1; i <= n; ++i) {
                scanf("%d", &a[i].len);
                a[i].pos = i;
            }
            sort(a + 1, a + 1 + n);
            for (int i = 1; i <= n; ++i) {
                sa[i] = a[i].pos;
                rk[ a[i].pos ] = i;
                add(i, a[i].len - a[i - 1].len);
            }
            int ch, x;
            while (q--) {
                scanf("%d%d", &ch, &x);
                if (ch == 1) {
                    int bp = rk[x];             // 原来的位置
                    int len = sum(bp);
                    int sp = lb(len + 1) - 1;   // 加一后的位置
                    swap(rk[ sa[bp] ], rk[ sa[sp] ]);
                    swap(sa[bp], sa[sp]);
                    add(sp, 1); add(sp + 1, -1);
                } else if (ch == 2) {
                    int ans = lb(x);
                    printf("%d
    ", n - ans + 1);
                } else {
                    int sp = lb(x);
                    add(sp, -1);
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    筛选IPV4地址
    linux查看磁盘空间大小df du fdisk stat命令
    编写shell脚本sum求1100累加和
    postman通过Cookies登录博客园
    Linux中mount挂载命令简洁使用方法
    linux如何查询文件及文件夹大小
    postman接口测试中添加不同的断言
    设计模式之状态模式
    Docker安装SQL Server
    架构漫谈读书笔记
  • 原文地址:https://www.cnblogs.com/wenruo/p/5452741.html
Copyright © 2011-2022 走看看