分块入门练习-2

练习-1及分块介绍


4.区间加法与求和

给出一个长为 N 的数列,以及 N个操作,操作涉及区间加法,区间求和。

数据范围:1 \leq n \leq 50000, ans,op\_val \in int

相对与第一题的单点查询,需要多维护一个区间的区间和属性sum[]

tag[]记录整块加的值,sum[]维护初始以及非整块操作时的块的和

#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.区间开方

给出一个长为 N的数列 \{a_i\}​​,以及 N 个操作,操作涉及区间开方,区间求和。

数据范围:1 \leq n \leq 50000, ans,op\_val \in int

乍一看因为区间值的变更没有相同规模根本无法区间维护

但可以注意到,一个数至多经过5次开方后变为1,或初始值为0,后续操作则没影响

故说明一个整块的更新至多5次,对于整块维护一个和sum[]的属性,以及flag[]记录区间是否还有大于1的数(是否还会更新)

PS:可以粗略看出(与实际测试)取块大小为N^{\frac{1}{3}}的值时间上更优

#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.在数列中不同位置插入数的维护(分块的重构)

给出一个长为N的数列,以及N个操作,操作涉及单点插入,单点询问(询问当前数列某位置的值),数据随机生成。

数据范围:1 \leq n \leq 100000, ans,op\_val \in int

如果要稳定的维护应该用平衡树并加上rank数据结构的扩张

先介绍一种操作rebuild(),即数列由于删除,插入等操作,需要重新选一个合适的块的大小进行重构

对于这题,先对初始划分的每个块维护一个vector<int> ve[],通过ve[].size()可以确定插入位置与询问的值

当某个ve[x].size()大于x*block(即这个块过大,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;
}

猜你喜欢

转载自blog.csdn.net/Hardict/article/details/82746555