數列分塊
阿新 • • 發佈:2020-09-01
數列分塊入門1
區間修改+單點查詢
可以維護一個lazy,分成好多塊之後。
區間內覆蓋的整個塊的直接用每個塊的lazy+c, 沒有覆蓋整個塊了就直接暴力修改。
複雜度\(O(n \ \sqrt n)\)
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define ll long long #define N 50010 #define M 1010 using namespace std; int n, tot, ans, block, num; int l[N], r[N], belong[N]; ll a[N], lazy[N]; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } void build() { block = sqrt(n); num = n / block; if (n % block) num++; for (int i = 1; i <= num; i++) l[i] = (i - 1) * block + 1, r[i] = i * block; r[num] = n; for (int i = 1; i <= n; i++) belong[i] = (i - 1) / block + 1; } void update(int x, int y, int c) { if (belong[x] == belong[y]) { for (int i = x; i <= y; i++) a[i] += c; return; } for (int i = x; i <= r[belong[x]]; i++) a[i] += c; for (int i = l[belong[y]]; i <= y; i++) a[i] += c; for (int i = belong[x] + 1; i <= belong[y] - 1; i++) lazy[i] += c; } ll query(int x) { return a[x] + lazy[belong[x]]; } int main() { n = read(); for (int i = 1; i <= n; i++) a[i] = read(); build(); for (int i = 1, opt, x, y, c; i <= n; i++) { opt = read(), x = read(), y = read(), c = read(); if (opt == 0) update(x, y, c); else printf("%lld\n", query(y)); } return 0; }
數列分塊入門2
區間修改
查詢區間l, r內有多少個比c 小的數
區間修改的話就按照上邊那種方法修改。
查詢我們可以一開始就給每一個塊內的答案排序,然後二分查詢就行了。
遇到不是完整的塊直接暴力修改,然後重新排序。
#include <cmath> #include <cstdio> #include <vector> #include <cstring> #include <iostream> #include <algorithm> #define ll long long #define N 50010 #define M 1010 using namespace std; int n, m, num, block; int l[N], r[N], belong[N]; int lazy[N], a[N]; vector<ll> vec[N]; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } void build() { block = sqrt(n); num = n / block; if (n % block) num++; r[num] = n; for (int i = 1; i <= n; i++) belong[i] = (i - 1) / block + 1, vec[belong[i]].push_back(a[i]); for (int i = 1; i <= num; i++) { l[i] = (i - 1) * block + 1, r[i] = i * block; sort(vec[i].begin(), vec[i].end()); } } void resort(int x) { vec[x].clear(); for (int i = l[x]; i <= r[x]; i++) vec[x].push_back(a[i]); sort(vec[x].begin(), vec[x].end()); } void update(int x, int y, int c) { if (belong[x] == belong[y]) { for (int i = x; i <= y; i++) a[i] += c; resort(belong[x]); return; } for (int i = x; i <= r[belong[x]]; i++) a[i] += c; for (int i = l[belong[y]]; i <= y; i++) a[i] += c; for (int i = belong[x] + 1; i <= belong[y] - 1; i++) lazy[i] += c; resort(belong[x]), resort(belong[y]); } int query(int x, int y, int c) { int ans = 0; if (belong[x] == belong[y]) { for (int i = x; i <= y; i++) if (a[i] + lazy[belong[x]] < c) ans++; return ans; } for (int i = x; i <= r[belong[x]]; i++) if (a[i] + lazy[belong[x]] < c) ans++; for (int i = l[belong[y]]; i <= y; i++) if (a[i] + lazy[belong[y]] < c) ans++; for (int i = belong[x] + 1; i <= belong[y] - 1; i++) ans += lower_bound(vec[i].begin(), vec[i].end(), c - lazy[i]) - vec[i].begin(); return ans; } int main() { n = read(); for (int i = 1; i <= n; i++) a[i] = read(); for (int i = 1, opt, x, y, c; i <= n; i++) { opt = read(), x = read(), y = read(), c = read(); if (opt == 0) update(x, y, c); else printf("%d\n", query(x, y, c * c)); } return 0; }
數列分塊入門3
區間修改+查詢區間內某個數的前驅
和上邊那種做法類似。
#include <cmath> #include <cstdio> #include <vector> #include <cstring> #include <iostream> #include <algorithm> #define ll long long #define N 100010 #define M 1010 using namespace std; int n, num, block; int l[N], r[N], lazy[N], belong[N], a[N]; vector<int> vec[N]; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } void build() { block = sqrt(n); num = n / block; if (n % block) num++; r[num] = n; for (int i = 1; i <= n; i++) belong[i] = (i - 1) / block + 1, vec[belong[i]].push_back(a[i]); for (int i = 1; i <= num; i++) { l[i] = (i - 1) * block + 1, r[i] = i * block; sort(vec[i].begin(), vec[i].end()); } } void resort(int x) { vec[x].clear(); for (int i = l[x]; i <= r[x]; i++) vec[x].push_back(a[i]); sort(vec[x].begin(), vec[x].end()); } void update(int x, int y, int c) { if (belong[x] == belong[y]) { for (int i = x; i <= y; i++) a[i] += c; resort(belong[x]); return; } for (int i = x; i <= r[belong[x]]; i++) a[i] += c; for (int i = l[belong[y]]; i <= y; i++) a[i] += c; for (int i = belong[x] + 1; i <= belong[y] - 1; i++) lazy[i] += c; resort(belong[x]), resort(belong[y]); } int query(int x, int y, int c) { int ans = -1; if (belong[x] == belong[y]) { for (int i = x; i <= y; i++) if (a[i] + lazy[belong[x]] < c) ans = max(ans, a[i] + lazy[belong[i]]); return ans; } for (int i = x; i <= r[belong[x]]; i++) if (a[i] + lazy[belong[i]] < c) ans = max(ans, a[i] + lazy[belong[i]]); for (int i = l[belong[y]]; i <= y; i++) if (a[i] + lazy[belong[i]] < c) ans = max(ans, a[i] + lazy[belong[i]]); for (int i = belong[x] + 1; i <= belong[y] - 1; i++) { int pos = lower_bound(vec[i].begin(), vec[i].end(), c - lazy[i]) - vec[i].begin(); if (pos >= 1) ans = max(ans, vec[i][pos - 1] + lazy[i]); } return ans; } int main() { n = read(); for (int i = 1; i <= n; i++) a[i] = read(); build(); for (int i = 1, opt, x, y, c; i <= n; i++) { opt = read(), x = read(), y = read(), c = read(); if (opt == 0) update(x, y, c); else printf("%d\n", query(x, y, c)); } return 0; }
數列分塊入門4
區間修改+區間查詢
可以開一個sum來存每一個塊裡的權值和
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define N 50010
#define M 1010
using namespace std;
int n, num, block;
int l[N], r[N], belong[N]; ll sum[N], lazy[N], a[N];
int read() {
int s = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
void build() {
block = sqrt(n);
num = n / block; if (n % block) num++;
r[num] = n;
for (int i = 1; i <= n; i++) belong[i] = (i - 1) / block + 1;
for (int i = 1; i <= num; i++)
l[i] = (i - 1) * block + 1, r[i] = i * block;
for (int i = 1; i <= num; i++)
for (int j = l[i]; j <= r[i]; j++)
sum[i] += a[j];
}
void update(int x, int y, ll c) {
if (belong[x] == belong[y]) {
for (int i = x; i <= y; i++) a[i] += c;
sum[belong[x]] += (y - x + 1) * c;
return;
}
for (int i = x; i <= r[belong[x]]; i++) a[i] += c;
for (int i = l[belong[y]]; i <= y; i++) a[i] += c;
for (int i = belong[x] + 1; i <= belong[y] - 1; i++) lazy[i] += c;
sum[belong[x]] += (r[belong[x]] - x + 1) * c;
sum[belong[y]] += (y - l[belong[y]] + 1) * c;
}
ll query(int x, int y, ll c) {
ll mod = c + 1, ans = 0;
if (belong[x] == belong[y]) {
for (int i = x; i <= y; i++)
ans = (ans + a[i] + lazy[belong[i]]) % mod;
return ans;
}
for (int i = x; i <= r[belong[x]]; i++)
ans = (ans + a[i] + lazy[belong[i]]) % mod;
for (int i = l[belong[y]]; i <= y; i++)
ans = (ans + a[i] + lazy[belong[i]]) % mod;
for (int i = belong[x] + 1; i <= belong[y] - 1; i++)
ans = (ans + sum[i] + block * lazy[i]) % mod;
return ans;
}
signed main() {
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
build();
for (int i = 1, opt, x, y, c; i <= n; i++) {
opt = read(), x = read(), y = read(), c = read();
if (opt == 0) update(x, y, c);
else printf("%lld\n", query(x, y, c));
}
return 0;
}
數列分塊入門5
區間開平方。
眾所周知\(\sqrt{\sqrt{\sqrt{\sqrt{\sqrt {2^{32}}}}}} = 1\)
所以一個區間開平方開5次就差不多是1或者0
這樣的區間不需要再開方。