算法
考虑求小于给定值的数的个数,可以给每个块再维护一个已经排好序的数组,整块加法对于这个块排好序的数组没有影响,零散块加法直接在原序列上加,再将零散块所处的整块从新排序。查询时零散块暴力查找,整块二分查找
代码
#include <bits/stdc++.h>
#define int long long
const int MAXN = 1e6;
int n;
int Num[MAXN];
struct block
{
int Left;
int Right;
int Size;
int tmp[400];
int cnt = 0;
int add = 0;
} Blocks[400];
int Pos[MAXN];
bool cmp(int a, int b);
void Sort(int x)
{
for (int i = Blocks[x].Left; i <= Blocks[x].Right; i++)
{
Blocks[x].tmp[++Blocks[x].cnt] = Num[i];
// Pos[i] = x;
}
std::sort(Blocks[x].tmp + 1, Blocks[x].tmp + Blocks[x].Size + 1);
}
void Split()
{
int Block_Size = sqrt(n);
int Block_Num = n / Block_Size;
if (n % Block_Size)
Block_Num++;
for (int i = 1; i < Block_Num; i++)
{
Blocks[i].Left = (i - 1) * Block_Size + 1;
Blocks[i].Right = i * Block_Size;
Blocks[i].Size = Block_Size;
Sort(i);
}
Blocks[Block_Num].Left = Blocks[Block_Num - 1].Right + 1;
Blocks[Block_Num].Right = n;
Blocks[Block_Num].Size = Blocks[Block_Num].Right - Blocks[Block_Num].Left + 1;
Sort(Block_Num);
for (int i = 1; i <= n; i++)
{
Pos[i] = (i - 1) / Block_Size + 1;
}
}
void Change(int Left, int Right, int Aim)
{
int Left_Block = Pos[Left], Right_block = Pos[Right];
if (Left_Block == Right_block)
{
for (int i = Left; i <= Right; i++)
{
Num[i] += Aim;
}
for (int i = Blocks[Left_Block].Left; i <= Blocks[Left_Block].Right; i++)
{
Blocks[Left_Block].tmp[i - Blocks[Left_Block].Left + 1] = Num[i];
}
std::sort(Blocks[Left_Block].tmp + 1, Blocks[Left_Block].tmp + Blocks[Left_Block].Size + 1);
}
else
{
for (int i = Left_Block + 1; i <= Right_block - 1; i++)
{
Blocks[i].add += Aim;
}
for (int i = Left; i <= Blocks[Left_Block].Right; i++)
{
Num[i] += Aim;
}
for (int i = Blocks[Left_Block].Left; i <= Blocks[Left_Block].Right; i++) //
{
Blocks[Left_Block].tmp[i - Blocks[Left_Block].Left + 1] = Num[i];
}
std::sort(Blocks[Left_Block].tmp + 1, Blocks[Left_Block].tmp + Blocks[Left_Block].Size + 1);
for (int i = Blocks[Right_block].Left; i <= Right; i++)
{
Num[i] += Aim;
Blocks[Right_block].tmp[i - Blocks[Right_block].Left + 1] = Num[i];
}
for (int i = Blocks[Right_block].Left; i <= Blocks[Right_block].Right; i++)
{
Blocks[Right_block].tmp[i - Blocks[Right_block].Left + 1] = Num[i];
}
std::sort(Blocks[Right_block].tmp + 1, Blocks[Right_block].tmp + Blocks[Right_block].Size + 1);
}
}
int Find(int Left, int Right, int Aim)
{
int Left_Block = Pos[Left], Right_block = Pos[Right];
int Ans = 0;
if (Left_Block == Right_block)
{
for (int i = Left; i <= Right; i++)
{
if (Num[i] + Blocks[Pos[i]].add < Aim)
Ans++;
}
}
else{
for (int i = Left_Block + 1; i <= Right_block - 1; i++)
{
Ans += std::lower_bound(Blocks[i].tmp + 1, Blocks[i].tmp + Blocks[i].cnt + 1, Aim - Blocks[i].add) - (Blocks[i].tmp) - 1; //
}
for (int i = Left; i <= Blocks[Left_Block].Right; i++)
{
if (Num[i] + Blocks[Left_Block].add < Aim)
Ans++;
}
for (int i = Blocks[Right_block].Left; i <= Right; i++)
{
if (Num[i] + Blocks[Right_block].add < Aim)
Ans++;
}
}
return Ans;
}
signed main()
{
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
{
scanf("%lld", &Num[i]);
}
Split();
for (int i = 1; i <= n; i++)
{
int op, l, r, c;
scanf("%lld %lld %lld %lld", &op, &l, &r, &c);
if (op == 0)
Change(l, r, c);
else
printf("%lld\n", Find(l, r, c * c));
}
return 0;
}
bool cmp(int a, int b)
{
return a < b;
}
/*
4
1 2 2 3
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2
3
0
2
*/
总结
查找数列中 \(\geq\) 一个数, 可以用 std::lower_bound(a + 1, a + len + 1, Aim) - (a)
, 注意最后 (a)
不加 \(1\)
分块处理时注意零散块更改会影响随后整块的更改
标签:零散,数列,分块,LOJ,整块,int,MAXN From: https://www.cnblogs.com/YzaCsp/p/18458537