gym344448C线段树/树状数组 离线查询区间小于等于k的数的数量

Posted hesorchen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gym344448C线段树/树状数组 离线查询区间小于等于k的数的数量相关的知识,希望对你有一定的参考价值。

题目

原地击剑

求解

其实就是求

∑ i = 1 n ∑ j = i + 1 n a i + i > = j & & a j − j < = i \\sum_{i=1}^{n}\\sum_{j=i+1}^{n} a_i+i>=j{\\&\\&} a_j-j<=i i=1nj=i+1nai+i>=j&&ajj<=i

两个条件可以写成: j < = a i + i & & a j − j < = i j<=a_i+i\\&\\&a_j-j<=i j<=ai+i&&ajj<=i

问题转化为,对于每个 a i a_i ai,要查询区间 [ i + 1 , a i + i ] [i+1,a_i+i] [i+1,ai+i]有多少数满足 a j − j < = i a_j-j<=i ajj<=i

a j − j a_j-j ajj作为新值,也就是查询区间小于等于 k k k的数的数量。在线查询可以用二分主席树等求解,这题没有强制在线,可以将询问按照k值从小到大排序,每次插入新值,用树状数组查询区间数量。

参考资料

代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;

struct Query
{
    int l, r, h, id;
    bool operator<(const Query nodes)
    {
        return h < nodes.h;
    }
} query[N];
struct Node
{
    int num, id;
    bool operator<(const Node nodes)
    {
        return num < nodes.num;
    }
} nodes[N];

int c[N];
int res[N];
int n;
int cas = 1;

int lowbit(int i)
{
    return i & -i;
}

void add(int i, int x)
{
    while (i <= n)
    {
        c[i] += x;
        i += lowbit(i);
    }
}

int ask(int i)
{
    int ans = 0;
    while (i)
    {
        ans += c[i];
        i -= lowbit(i);
    }
    return ans;
}
void f()
{
    int last = 1;
    for (int i = 1; i <= n; i++)
    {
        if (query[i].l > query[i].r)
            continue;
        while (query[i].h >= nodes[last].num && last <= n)
        {
            add(nodes[last].id, 1);
            last++;
        }
        res[query[i].id] = ask(query[i].r) - ask(query[i].l - 1);
    }

    long long ans = 0;
    for (int i = 1; i <= n; i++)
    {
        ans += res[i];
    }
    printf("%lld\\n", ans);
}
void solve()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &nodes[i].num);
        nodes[i].id = i;

        query[i].id = i;
        query[i].l = i + 1;
        query[i].r = min(n, nodes[i].num + i);
        query[i].h = i;
        nodes[i].num = i - nodes[i].num;
    }
    sort(nodes + 1, nodes + 1 + n);
    sort(query + 1, query + 1 + n);
    f();
}
int main()
{
    solve();
    return 0;
}

其实也可以按照 a i a_i ai从大到小插入,然后区间查询即可。这样一来,当前插入的点可以碰到的点前面插入的点也一定可以碰到该点。

以上是关于gym344448C线段树/树状数组 离线查询区间小于等于k的数的数量的主要内容,如果未能解决你的问题,请参考以下文章

P3372 模板线段树 1(区间修改区间查询)(树状数组)

树状数组与线段树

(树状数组+离线查询)HDU 4417 - Super Mario

树状数组的建树 单点修改 单点查询 区间修改 区间查询

树状数组实现区间修改+区间查询

算法系列学习线段树vs树状数组 单点修改,区间查询 [kuangbin带你飞]专题七 线段树 A - 敌兵布阵