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

本文探讨了一种名为“原地击剑”的算法问题求解方法。通过转换问题描述为区间查询的形式,利用树状数组进行离线处理,实现对特定条件下元素数量的有效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

原地击剑

求解

其实就是求

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

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

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

j − a j j-a_j jaj作为新值,也就是查询区间小于等于 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从大到小插入,然后区间查询即可。这样一来,当前插入的点可以碰到的点前面插入的点也一定可以碰到该点。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hesorchen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值