Codeforces 1454E - Number of Simple Paths (dfs)
阿新 • • 發佈:2020-11-25
分 塊
目錄模板題
沒有專門模板,就直接用線段樹1啦,其實都一樣
資料結構講解
說句良心話,分塊真的不難理解,甚至比樹狀陣列,線段樹更容易,只是效率偏低
一句話概括分塊:大段維護,區域性樸素
怎麼理解呢?
以模板題(區間求和,區間更新)為例,將原數列劃分成t個塊,每個塊的大小不超過根號n,我們預處理出每一個塊內的和,另外開一個add[]
(類似於線段樹懶標記,但沒有下傳操作),儲存每個塊內增加的值(其實這裡講的不是很清楚,等下就明白了)
預處理
變數說明:
int n , m , t; //n,m(見題目),t:段(塊)的數量 ll a[nn] , sum[nn] , add[nn]; //a:見題目,sum:每一段(塊)內a的和,add:等下講 int L[nn] , R[nn]; //L:每一段(塊)的左邊界,R:每一段的右邊界 int pos[nn]; //pos[i]:第i個點屬於哪一個塊
void Init() { t = sqrt(n);//此時的t既是段的數量,又是段的長度 for(int i = 1 ; i <= t ; i++) L[i] = (i - 1) * t + 1,//這裡可以自行推導一下 R[i] = i * t; if(R[t] < n) {//處理剩下的點 ++t; L[t] = R[t - 1] + 1 , R[t] = n; } for(int i = 1 ; i <= t ; i++) for(int j = L[i] ; j <= R[i] ; j++) pos[j] = i , sum[i] += a[j]; }
區間修改
對於1操作(區間修改):[l,r]上的每一個數加上dat(變數與題目不同)
-
若l,r同屬一個分塊,直接暴力,將
a[l~r]
加上dat -
否則,設l處於第p個分塊,r屬於第q個分塊,
-
\(對於i\in[p+1,q+1],add_i+=dat\)
-
對於開頭,結尾不足一整段的兩部分,直接用樸素方法更新即可
-
看程式碼:
void change(int l , int r , ll dat) { int p = pos[l] , q = pos[r]; if(p == q) {//l,r同屬一個分塊 for(int i = l ; i <= r ; i++)//暴力 a[i] += dat; sum[p] += dat * (r - l + 1);//勿忘更新sum return; } for(int i = l ; i <= R[p] ; i++)//對於開頭不足一段的部分,暴力 a[i] += dat; sum[p] += (R[p] - l + 1) * dat; for(int i = p + 1 ; i <= q - 1 ; i++)//中間,加到add即可,注意不需要更新sum,更具體的原因,查詢部分會知道 add[i] += dat; for(int i = L[q] ; i <= r ; i++)//結尾不足一段,暴力 a[i] += dat; sum[q] += (r - L[q] + 1) * dat; }
到此,你應該理解add陣列的用處了
區間查詢
基本原理同區間修改,也是頭尾不足一段的直接暴力,中間整段查詢,這裡不再贅述
ll query(int l , int r) {
int p = pos[l] , q = pos[r];
ll ans = 0;
if(p == q) {
for(int i = l ; i <= r ; i++)
ans += a[i];
ans += add[p] * (r - l + 1);
return ans;
}
for(int i = l ; i <= R[p] ; i++)
ans += a[i];
ans += add[p] * (R[p] - l + 1);
for(int i = p + 1 ; i <= q - 1 ; i++)
ans += sum[i] + add[i] * (R[i] - L[i] + 1);
for(int i = L[q] ; i <= r ; i++)
ans += a[i];
ans += add[q] * (r - L[q] + 1);
return ans;
}
模板題程式碼
#include <iostream>
#include <cstdio>
#include <cmath>
#define nn 100010
#define ll long long
using namespace std;
ll read() {
ll re = 0;
bool sig = false;
char c;
do
if((c = getchar()) == '-')sig = true;
while(c < '0' || c > '9');
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return sig ? -re : re;
}
int n , m , t;
ll a[nn] , sum[nn] , add[nn];
int L[nn] , R[nn];
int pos[nn];
void Init() {
t = sqrt(n);
for(int i = 1 ; i <= t ; i++)
L[i] = (i - 1) * t + 1,
R[i] = i * t;
if(R[t] < n) {
++t;
L[t] = R[t - 1] + 1 , R[t] = n;
}
for(int i = 1 ; i <= t ; i++)
for(int j = L[i] ; j <= R[i] ; j++)
pos[j] = i , sum[i] += a[j];
}
void change(int l , int r , ll dat) {
int p = pos[l] , q = pos[r];
if(p == q) {
for(int i = l ; i <= r ; i++)
a[i] += dat;
sum[p] += dat * (r - l + 1);
return;
}
for(int i = l ; i <= R[p] ; i++)
a[i] += dat;
sum[p] += (R[p] - l + 1) * dat;
for(int i = p + 1 ; i <= q - 1 ; i++)
add[i] += dat;
for(int i = L[q] ; i <= r ; i++)
a[i] += dat;
sum[q] += (r - L[q] + 1) * dat;
}
ll query(int l , int r) {
int p = pos[l] , q = pos[r];
ll ans = 0;
if(p == q) {
for(int i = l ; i <= r ; i++)
ans += a[i];
ans += add[p] * (r - l + 1);
return ans;
}
for(int i = l ; i <= R[p] ; i++)
ans += a[i];
ans += add[p] * (R[p] - l + 1);
for(int i = p + 1 ; i <= q - 1 ; i++)
ans += sum[i] + add[i] * (R[i] - L[i] + 1);
for(int i = L[q] ; i <= r ; i++)
ans += a[i];
ans += add[q] * (r - L[q] + 1);
return ans;
}
int main() {
n = read(); m = read();
for(int i = 1 ; i <= n ; i++)
a[i] = read();
Init();
for(int i = 1 ; i <= m ; i++) {
int l , r , dat;
if(read() == 1) {
l = read(), r = read(), dat = read();
change(l , r , dat);
}
else {
l = read(), r = read();
printf("%lld\n" , query(l , r));
}
}
return 0;
}