4.区间加法与求和
给出一个长为 的数列,以及 个操作,操作涉及区间加法,区间求和。
数据范围:
相对与第一题的单点查询,需要多维护一个区间的区间和属性
记录整块加的值,维护初始以及非整块操作时的块的和
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <set>
using namespace std;
using LL = long long;
LL N;
LL block, val[50005], blg[50005], tag[1005], sum[1005];
void add(int, int, int);
LL query(int, int, LL);
int main(){
ios::sync_with_stdio(false);
int i;
cin >> N, block = sqrt(N);
for(i = 1; i <= N; i++) cin >> val[i];
for(i = 1; i <= N; i++){
blg[i] = (i - 1) / block + 1;
sum[blg[i]] += val[i];
}
for(i = 1; i <= N; i++){
int op, x, y, key;
cin >> op >> x >> y >> key;
if(op == 0) add(x, y, key);
else cout << query(x, y, 1LL * key) << endl;
}
return 0;
}
void add(int l, int r, int key){
int i, top;
for(i = l, top = min(blg[l] * block, 1LL * r); i <= top; i++)
val[i] += key, sum[blg[i]] += key;
if(blg[l] != blg[r])
for(i = (blg[r] - 1) * block + 1; i <= r; i++)
val[i] += key, sum[blg[i]] += key;
for(i = blg[l] + 1; i <= blg[r] - 1; i++)
tag[i] += key;
}
LL query(int l, int r, LL c){
LL MOD = c + 1, res = 0;
int i, top;
for(i = l, top = min(blg[l] * block, 1LL * r); i <= top; i++)
res += val[i] + tag[blg[i]], res %= MOD;
if(blg[l] != blg[r])
for(i = (blg[r] - 1) * block + 1; i <= r; i++)
res += val[i] + tag[blg[i]], res %= MOD;
for(i = blg[l] + 1; i <= blg[r] - 1; i++)
res += (sum[i] + block * tag[i]) % MOD, res %= MOD;
return res;
}
5.区间开方
给出一个长为 的数列 ,以及 个操作,操作涉及区间开方,区间求和。
数据范围:
乍一看因为区间值的变更没有相同规模根本无法区间维护
但可以注意到,一个数至多经过5次开方后变为1,或初始值为0,后续操作则没影响
故说明一个整块的更新至多5次,对于整块维护一个和的属性,以及记录区间是否还有大于1的数(是否还会更新)
PS:可以粗略看出(与实际测试)取块大小为的值时间上更优
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
using LL = long long;
LL N;
LL block, val[50005], blg[50005], sum[10005];
void update(int, int);
LL query(int, int);
int main(){
ios::sync_with_stdio(false);
int i;
cin >> N, block = pow(N, 0.333);
for(i = 1; i <= N; i++) cin >> val[i];
for(i = 1; i <= N; i++){
blg[i] = (i - 1) / block + 1;
sum[blg[i]] += val[i];
}
for(i = 1; i <= N; i++){
int op, x, y, key;
cin >> op >> x >> y >> key;
if(op == 0) update(x, y);
else cout << query(x, y) << endl;
}
return 0;
}
bool flag[10005];
void solve(int block_id);
void update(int l, int r){
int i, top;
if(!flag[blg[l]])
for(i = l, top = min(blg[l] * block, 1LL * r); i <= top; i++)
sum[blg[i]] -= val[i], val[i] = sqrt(val[i]), sum[blg[i]] += val[i];
if(blg[l] != blg[r] && !flag[blg[r]])
for(i = (blg[r] - 1) * block + 1; i <= r; i++)
sum[blg[i]] -= val[i], val[i] = sqrt(val[i]), sum[blg[i]] += val[i];
for(i = blg[l] + 1; i <= blg[r] - 1; i++)
solve(i);
}
LL query(int l, int r){
LL res = 0;
int i, top;
for(i = l, top = min(blg[l] * block, 1LL * r); i <= top; i++)
res += val[i];
if(blg[l] != blg[r])
for(i = (blg[r] - 1) * block + 1; i <= r; i++)
res += val[i];
for(i = blg[l] + 1; i <= blg[r] - 1; i++)
res += sum[i];
return res;
}
void solve(int x){
if(flag[x]) return;
flag[x] = true;
int i, top;
sum[x] = 0;
for(i = (x - 1) * block + 1, top = min(x * block, 1LL * N); i <= top; i++){
val[i] = sqrt(val[i]);
sum[blg[i]] += val[i];
if(val[i] > 1) flag[x] = false;
}
}
6.在数列中不同位置插入数的维护(分块的重构)
给出一个长为的数列,以及个操作,操作涉及单点插入,单点询问(询问当前数列某位置的值),数据随机生成。
数据范围:
如果要稳定的维护应该用平衡树并加上数据结构的扩张
先介绍一种操作,即数列由于删除,插入等操作,需要重新选一个合适的块的大小进行重构
对于这题,先对初始划分的每个块维护一个,通过可以确定插入位置与询问的值
当某个大于(即这个块过大,x取10就行)就进行重构
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
int N, M;
int block;
vector<int> ve[1005];
int trans[200005];
void insert(int x, int key);
pair<int, int> query(int);
int main(){
ios::sync_with_stdio(false);
int i;
cin >> N, block = sqrt(N);
for(i = 1; i <= N; i++){
int x;
cin >> x;
ve[(i - 1) / block + 1].push_back(x);
}
M = (N - 1) / block + 1;
for(i = 1; i <= N; i++){
int op, x, y, key;
cin >> op >> x >> y >> key;
if(op == 0) insert(x, y);
else{
auto tmp = query(y);
cout << ve[tmp.first][tmp.second] << endl;
}
}
return 0;
}
void rebuild();
void insert(int x, int key){
pair<int, int> tmp = query(x);
ve[tmp.first].insert(ve[tmp.first].begin() + tmp.second, key);
if(ve[tmp.first].size() > 20 * block){
rebuild();
}
}
pair<int, int> query(int x){
int i = 1;
while(x > ve[i].size())
x -= ve[i].size(), i++;
return make_pair(i, x - 1);
}
void rebuild(){
int i, k;
for(i = 1, k = 0; i <= M; i++){
for(auto j:ve[i])
trans[++k] = j;
ve[i].clear();
}
block = sqrt(k);
for(i = 1; i <= k; i++)
ve[(i - 1) / block + 1].push_back(trans[i]);
M = (k - 1) / block + 1;
}