BZOJ3289 Mato的文件管理-莫队算法

Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号。为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问。Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料。Mato有一个习惯,他总是从文件大小从小到大看资料。他先把要看的文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序。排序程序可以在1单位时间内交换2个相邻的文件(因为加密需要,不能随机访问)。Mato想要使文件交换次数最小,你能告诉他每天需要交换多少次吗?

Input

第一行一个正整数n,表示Mato的资料份数。
第二行由空格隔开的n个正整数,第i个表示编号为i的资料的大小。
第三行一个正整数q,表示Mato会看几天资料。
之后q行每行两个正整数l、r,表示Mato这天看[l,r]区间的文件。

Output

q行,每行一个正整数,表示Mato这天需要交换的次数。

Sample Input
4
1 4 2 3
2
1 2
2 4
Sample Output
0
2
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5;
int len_block;
struct node {
    int l, r, block, id;
    node() {}
    node(int l, int r, int id) :l(l), r(r), id(id) {
        block = l / len_block;
    }
    bool operator <(const node&x)const {
        if (block == x.block) return r < x.r;
        return block < x.block;
    }
}query[maxn];
int a[maxn], b[maxn], ans[maxn], c[maxn];
int n, q;
void add(int x, int val)
{
    for (int pos = x; pos <= n; pos += pos&(-pos))
        c[pos] += val;
}
int que(int x)
{
    int ret = 0;
    for (int pos = x; pos > 0; pos -= pos&(-pos))
        ret += c[pos];
    return ret;
}
int main()
{
    scanf("%d", &n);
    len_block = int(sqrt(n));
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];
    sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; i++) b[i] = lower_bound(a + 1, a + n + 1, b[i]) - a;
    //for (int i = 1; i <= n; i++) printf("%d ",b[i]);
    scanf("%d", &q);
    for (int i = 1; i <= q; i++) {
        int l, r;
        scanf("%d%d", &l, &r);
        query[i] = node(l, r, i);
    }
    sort(query + 1, query + q + 1);
    int l = 1, r = 1;
    int sum = 0;
    add(b[1], 1);
    for (int i = 1; i <= q; i++) {
        node &qr = query[i];
        while (r < qr.r) {
            r++;
            //int temp = que(b[r]);
            sum += r - l - que(b[r]);
            add(b[r], 1);
        }
        while (l > qr.l) {
            l--;
            //int temp = que(b[l]);
            sum += que(b[l]);
            add(b[l], 1);
        }
        while (r > qr.r) {
            //int temp = que(b[r]);
            sum -= r - l - que(b[r]-1);
            //b[r]-1是因为b[r]这个数已经加过
            //这里需要减去的是比b[r]大的数的个数
            //不减一就成了大于等于
            add(b[r], -1);
            r--;
        }
        while (l < qr.l) {
            //int temp = que(b[l]);
            sum -= que(b[l] - 1);
            //b[l]-1是因为b[l]这个数已经加过
            //这里需要减去的是比b[l]小的数的个数
            //不减一就成了小于等于
            add(b[l], -1);
            l++;
        }
        ans[qr.id] = sum;
    }
    for (int i = 1; i <= q; i++) printf("%d\n", ans[i]);
    return 0;
}

弱鸡写了好久,没推出来转移的式子

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值