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=1n∑j=i+1nai+i>=j&&aj−j<=i
两个条件可以写成: j < = a i + i & & a j − j < = i j<=a_i+i\\&\\&a_j-j<=i j<=ai+i&&aj−j<=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 aj−j<=i
将 a j − j a_j-j aj−j作为新值,也就是查询区间小于等于 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的数的数量的主要内容,如果未能解决你的问题,请参考以下文章