分块
6277. 数列分块入门 1
分块思想
我们把每m个元素分成一块,所以我们总共的块数就是 块,一般情况下我们取 .对于区间加操作,我们可以先暴力左右两边,然后对于中间的整块的部分的加减,我们累加在块的标记上,然后我们每次查询的时候只要,每个元素的值加上这个块的标记值,就可以得到我们的答案了。
复杂度分析
每次操作我们修改的最多的元素最多就是 级别的个数,时间复杂读也就是 级别的,查询的时间复杂度是 的,最多有 个操作,整体上是 的,于是一个暴力而优美的分块算法就出现了,简单的思想, 级别的优化。
代码
/*
Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;
inline ll read() {
ll f = 1, x = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return f * x;
}
void print(ll x) {
if(x < 10) {
putchar(x + 48);
return ;
}
print(x / 10);
putchar(x % 10 + 48);
}
const int N = 5e4 + 10;
int value[N], bl[N], tag[N], block, n;
void add(int l, int r, int c) {
for(int i = l; i <= bl[l] * block && i <= r; i++) {//对前面的部分进行暴力修改。
value[i] += c;
}
if(bl[l] != bl[r]) {//如果这两个块不在同一个分块中才要进行后面的暴力修改,否则将会重复累加。
for(int i = block * (bl[r] - 1) + 1; i <= r; i++) {
value[i] += c;
}
}
for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {//对每个块进行区间修改。
tag[i] += c;
}
//严格来说每次修改的复杂度最多将会是3 * sqrt(n)
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = read(); block = sqrt(n);//block是分块的大小
for(int i = 1; i <= n; i++) {
value[i] = read();
bl[i] = (i - 1) / block + 1;//每个位置在分块中的位置。
}
for(int i = 1; i <= n; i++) {
int op = read(), l = read(), r = read(), c = read();
if(!op) {
add(l, r, c);
}
else {
printf("%d\n", value[r] + tag[bl[r]]);//他当前的值加上分块的累加值。
}
}
return 0;
}
6278. 数列分块入门 2
想法
查询操作:对于每一个块我们开一个数组来维护其有序的状态,所以对于每一个整块我们可以通过二分去得到我们的答案,对于两头的块,我们可以暴力去得到满足条件的数。
修改操作:同样的对于整块的我们还是在一个区间标记加上其修改值,对于两头的操作我们需要额外单独的操作,记得操作完之后要把原来的存区间有序的数组重新排序,得到一个新的区间有序数组。
代码
/*
Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;
inline ll read() {
ll f = 1, x = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return f * x;
}
void print(ll x) {
if(x < 10) {
putchar(x + 48);
return ;
}
print(x / 10);
putchar(x % 10 + 48);
}
const int N = 5e4 + 10;
int value[N], bl[N], tag[N], n, block, m;
vector<int> elem[N];
void reset(int x) {//最好还是写一个函数,一开始我就是没写函数然后wa了一次。
elem[x].clear();
for(int i = (x - 1) * block + 1; i <= x * block && i <= n; i++)
elem[x].pb(value[i]);
sort(elem[x].begin(), elem[x].end());
}
void add(int l, int r, int x) {
for(int i = l; i <= r && i <= bl[l] * block; i++)
value[i] += x;
reset(bl[l]);
if(bl[l] != bl[r]) {
for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
value[i] += x;
reset(bl[r]);
}
for(int i = bl[l] + 1; i <= bl[r] - 1; i++)
tag[i] += x;
}
int query(int l, int r, int x) {
int sum = 0;
for(int i = l; i <= r && i <= bl[l] * block; i++)
if(value[i] + tag[bl[i]] < x)
sum++;
if(bl[l] != bl[r])
for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
if(value[i] + tag[bl[i]] < x)
sum++;
for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {
sum += lower_bound(elem[i].begin(), elem[i].end(), x - tag[i]) - elem[i].begin();
}
return sum;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = read(); block = sqrt(n);
for(int i = 1; i <= n; i++) {
value[i] = read();
bl[i] = (i + block - 1) / block;
elem[bl[i]].pb(value[i]);
}
m = bl[n];
for(int i = 1; i <= m; i++) sort(elem[i].begin(), elem[i].end());
for(int i = 1; i <= n; i++) {
int op = read(), l = read(), r = read(), c = read();
if(op & 1) {
printf("%d\n", query(l, r, c * c));
}
else {
add(l, r, c);
}
}
return 0;
}
6279. 数列分块入门 3
想法
这题应该是跟上一个类似,就是 操作稍微变化一下。
用上面的代码修改的时候要注意,这题数据变大了,需要改成 ,不然就跟我一样入坑了。
代码
/*
Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;
inline ll read() {
ll f = 1, x = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return f * x;
}
void print(ll x) {
if(x < 10) {
putchar(x + 48);
return ;
}
print(x / 10);
putchar(x % 10 + 48);
}
const int N = 1e5 + 10;
int value[N], bl[N], tag[N], n, block, m;
vector<int> elem[N];
void reset(int x) {
elem[x].clear();
for(int i = (x - 1) * block + 1; i <= x * block && i <= n; i++)
elem[x].pb(value[i]);
sort(elem[x].begin(), elem[x].end());
}
void add(int l, int r, int x) {
for(int i = l; i <= r && i <= bl[l] * block; i++)
value[i] += x;
reset(bl[l]);
if(bl[l] != bl[r]) {
for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
value[i] += x;
reset(bl[r]);
}
for(int i = bl[l] + 1; i <= bl[r] - 1; i++)
tag[i] += x;
}
ll query(int l, int r, int x) {
ll ans = -1e10;
for(int i = l; i <= r && i <= bl[l] * block; i++)
if(value[i] + tag[bl[i]] < x && value[i] + tag[bl[i]] > ans)
ans = value[i] + tag[bl[i]];
if(bl[l] != bl[r])
for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
if(value[i] + tag[bl[i]] < x && value[i] + tag[bl[i]] > ans)
ans = value[i] + tag[bl[i]];
for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {
auto p = lower_bound(elem[i].begin(), elem[i].end(), x - tag[i]);
if(p == elem[i].begin()) continue;
p--;
ans = max(ans, 1ll * (*p + tag[i]));
}
return ans == -1e10 ? -1 : ans;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = read(); block = sqrt(n);
for(int i = 1; i <= n; i++) {
value[i] = read();
bl[i] = (i + block - 1) / block;
elem[bl[i]].pb(value[i]);
}
m = bl[n];
for(int i = 1; i <= m; i++) sort(elem[i].begin(), elem[i].end());
for(int i = 1; i <= n; i++) {
int op = read(), l = read(), r = read(), c = read();
if(op & 1) {
printf("%d\n", query(l, r, c));
}
else {
add(l, r, c);
}
}
return 0;
}
6280. 数列分块入门 4
想法
仿照题二,我们可以再引入一个数组来代表当前块的总和,之后的操作就变得简单了。
代码
/*
Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;
inline ll read() {
ll f = 1, x = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return f * x;
}
void print(ll x) {
if(x < 10) {
putchar(x + 48);
return ;
}
print(x / 10);
putchar(x % 10 + 48);
}
const int N = 1e5 + 10;
ll a[N], sum[N], tag[N];
int n, block, bl[N];
void update(int l, int r, int x) {
for(int i = l; i <= bl[l] * block && i <= r; i++) {
a[i] += x;
sum[bl[i]] += x;
}
if(bl[l] != bl[r]) {
for(int i = (bl[r] - 1) * block + 1; i <= r; i++) {
a[i] += x;
sum[bl[i]] += x;
}
}
for(int i = bl[l] + 1; i <= bl[r] - 1; i++)
tag[i] += x;
}
int query(int l, int r, int mod) {
ll ans = 0;
for(int i = l; i <= bl[l] * block && i <= r; i++) {
ans = (ans + a[i] + tag[bl[i]]) % mod;
}
if(bl[l] != bl[r]) {
for(int i = (bl[r] - 1) * block + 1; i <= r; i++) {
ans = (ans + a[i] + tag[bl[i]]) % mod;
}
}
for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {
ans = (ans + sum[i] + tag[i] * block) % mod;
}
return ans;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = read(); block = sqrt(n);
for(int i = 1; i <= n; i++) {
a[i] = read();
bl[i] = (i + block - 1) / block;
sum[bl[i]] += a[i];
}
for(int i = 1; i <= n; i++) {
int op = read(), l = read(), r = read(), c = read();
if(op & 1) {
printf("%d\n", query(l, r, c + 1));
}
else {
update(l, r, c);
}
}
return 0;
}
6281. 数列分块入门 5
想法
一开始看到这个题目感觉分块无从下手啊,区间开方操作,结果一看他们ac代码,原来是开根号,,,
既然是开根号那就简单了,学过线段树应该写过这类题,我们只要加一个区间tag来标记区间内所有的数是否全部变成0和1即可。然后再加一个区间和,对于tag标记为,区间所有的数都是0 和1的我们可以直接查询区间和,也可以跳过他的开方显然有 ,
代码
/*
Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;
inline ll read() {
ll f = 1, x = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return f * x;
}
void print(ll x) {
if(x < 10) {
putchar(x + 48);
return ;
}
print(x / 10);
putchar(x % 10 + 48);
}
const int N = 1e5 + 10;
int a[N], sum[N], tag[N];
int n, block, bl[N];
void update(int l, int r) {
for(int i = l; i <= r && i <= bl[l] * block; i++) {
if(a[i] == 0 || a[i] == 1) continue;
sum[bl[i]] -= a[i];
a[i] = sqrt(a[i]);
sum[bl[i]] += a[i];
if(a[i] == 1 || a[i] == 0) tag[bl[i]]++;
}
if(bl[l] != bl[r]) {
for(int i = (bl[r] - 1) * block + 1; i <= r; i++) {
if(a[i] == 0 || a[i] == 1) continue;
sum[bl[i]] -= a[i];
a[i] = sqrt(a[i]);
sum[bl[i]] += a[i];
if(a[i] == 1 || a[i] == 0) tag[bl[i]]++;
}
}
for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {
if(tag[i] == block) continue;
else {
for(int j = (i - 1) * block + 1; j <= i * block; j++) {
if(a[j] == 0 || a[j] == 1) continue;
sum[bl[j]] -= a[j];
a[j] = sqrt(a[j]);
sum[bl[j]] += a[j];
if(a[i] == 1 || a[i] == 0) tag[bl[i]]++;
}
}
}
}
int query(int l, int r) {
int ans = 0;
for(int i = l; i <= r && i <= bl[l] * block; i++)
ans += a[i];
if(bl[l] != bl[r]) {
for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
ans += a[i];
}
for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {
if(tag[i] == block) ans += sum[i];
else {
for(int j = (i - 1) * block + 1; j <= i * block; j++)
ans += a[j];
}
}
return ans;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = read(); block = sqrt(n);
for(int i = 1; i <= n; i++) {
a[i] = read();
bl[i] = (i + block - 1) / block;
sum[bl[i]] += a[i];
if(a[i] == 0 || a[i] == 1) tag[bl[i]]++;
}
for(int i = 1; i <= n; i++) {
int op = read(), l = read(), r = read(), c = read();
if(op & 1) {
printf("%d\n", query(l, r));
}
else {
update(l, r);
}
}
return 0;
}
6282. 数列分块入门 6
想法
这里引入了一个暴力的 ,不愧是分块,依旧是如此的暴力。
代码
/*
Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;
inline ll read() {
ll f = 1, x = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return f * x;
}
void print(ll x) {
if(x < 10) {
putchar(x + 48);
return ;
}
print(x / 10);
putchar(x % 10 + 48);
}
const int N = 2e5 + 10;
int value[N], n, m, block;
vector<int> a[N];
pii query(int x) {
int now = 1;
while(x > a[now].size()) {
x -= a[now].size(), now++;
}
return mp(now, x - 1);
}
void rebuild() {
int num = 0;
for(int i = 1; i <= m; i++) {
for(int j = 0; j < a[i].size(); j++) {
value[++num] = a[i][j];
}
a[i].clear();//注意一定要clear,我就在这里找bug找了半小时。
}
block = sqrt(num), m = (num + block - 1) / block;
for(int i = 1; i <= num; i++) {
a[(i + block - 1) / block].pb(value[i]);
}
}
void insert(int pos, int value) {
pii p = query(pos);
a[p.first].insert(a[p.first].begin() + p.second, value);
if(a[p.first].size() > 20 * block)
rebuild();
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = read();
block = sqrt(n), m = (n + block - 1) / block;
for(int i = 1; i <= n; i++) {
value[i] = read();
a[(i + block - 1) / block].pb(value[i]);
}
for(int i = 1; i <= n; i++) {
int op = read(), l = read(), r = read(), c = read();
if(op & 1) {
pii ans = query(r);
printf("%d\n", a[ans.first][ans.second]);
}
else {
insert(l, r);
}
}
return 0;
}
6283. 数列分块入门 7
想法
暴力两头,区间操作中间。
代码
/*
Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;
inline ll read() {
ll f = 1, x = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return f * x;
}
void print(ll x) {
if(x < 10) {
putchar(x + 48);
return ;
}
print(x / 10);
putchar(x % 10 + 48);
}
const int N = 1e5 + 10, mod = 1e4 + 7;
int a[N], add[N], mult[N], bl[N], n, block;
void init(int bl) {
for(int i = (bl - 1) * block + 1; i <= bl * block; i++) {
a[i] = (a[i] * mult[bl]) % mod;
a[i] = (a[i] + add[bl]) % mod;
}
mult[bl] = 1, add[bl] = 0;
}
void Add(int l, int r, int x) {
init(bl[l]);
for(int i = l; i <= r && i <= bl[l] * block; i++)
a[i] = (a[i] + x) % mod;
if(bl[l] != bl[r]) {
init(bl[r]);
for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
a[i] = (a[i] + x) % mod;
}
for(int i = bl[l] + 1; i <= bl[r] - 1; i++)
add[i] = (add[i] + x) % mod;
}
void Mult(int l, int r, int x) {
init(bl[l]);
for(int i = l; i <= r && i <= bl[l] * block; i++)
a[i] = (a[i] * x) % mod;
if(bl[l] != bl[r]) {
init(bl[r]);
for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
a[i] = (a[i] * x) % mod;
}
for(int i = bl[l] + 1; i <= bl[r] - 1; i++)
mult[i] = (mult[i] * x) % mod, add[i] = (add[i] * x) % mod;
}
int query(int pos) {
return (((a[pos] * mult[bl[pos]])) % mod + add[bl[pos]]) % mod;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = read();
block = sqrt(n);
for(int i = 1; i <= n; i++) {
a[i] = read() % mod;
bl[i] = (i + block - 1) / block;
mult[i] = 1;
}
for(int i = 1; i <= n; i++) {
int op = read(), l = read(), r = read(), x = read();
if(!op) {
Add(l, r, x);
} else if(op & 1) {
Mult(l, r, x);
} else {
printf("%d\n", query(r));
}
}
return 0;
}