1. 程式人生 > >分塊入門練習-2

分塊入門練習-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;
}