数列分块入门 1
区间加 + 单点查询
#include #include#include
using namespacestd;const int N = 5e4 + 10;#define gc getchar()inlineintread() {int x = 0; char c =gc;while(c < '0' || c > '9') c =gc;while(c >= '0' && c <= '9') x = x * 10 + c - '0', c =gc;returnx;
}intA[N], Add[N], bel[N];intn, block, cnt;void Sec_G(int x, int y, intw) {if(bel[x] == bel[y]) for(int i = x; i <= y; i ++) A[i] +=w;else{for(int i = x; i <= bel[x] * block; i ++) A[i] +=w;for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] +=w;
}for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] +=w;
}intmain() {
n=read();
block=sqrt(n);for(int i = 1; i <= n; i ++) A[i] =read();for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1;if(n % block) cnt = n / block + 1;else cnt = n /block;int T =n;while(T --) {int opt = read(), l = read(), r = read(), c =read();if(!opt) Sec_G(l, r, c);else cout << A[r] + Add[bel[r]] <
}return 0;
}
数列分块入门 2
区间加法,询问区间内小于某个值 x的元素个数
用B[]记录A[], B[]数组中为排好序的A[]的映射
那么每次可以对每一块进行二分查找
#include #include#include#include
using namespacestd;const int N = 5e4 + 10;#define gc getchar()inlineintread() {int x = 0; char c =gc;while(c < '0' || c > '9') c =gc;while(c >= '0' && c <= '9') x = x * 10 + c - '0', c =gc;returnx;
}intA[N], B[N], Add[N], bel[N];intn, block, cnt;void Work_sort(intx) {int l = (x - 1) * block + 1, r = min(l + block - 1, n);for(int i = l; i <= r; i ++) B[i] =A[i];
sort(B+ l, B + r + 1);
}void Sec_G(int x, int y, intw) {if(bel[x] ==bel[y]) {for(int i = x; i <= y; i ++) A[i] +=w;
Work_sort(bel[x]);
}else{for(int i = x; i <= bel[x] * block; i ++) A[i] +=w; Work_sort(bel[x]);for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] +=w; Work_sort(bel[y]);
}for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] +=w;
}
inlineint Calc(int x, intw) {int l = (x - 1) * block + 1, r = min(l + block - 1, n), ret = 0;while(l <=r) {int mid = (l + r) >> 1;if(B[mid] + Add[x] < w) ret = mid, l = mid + 1;else r = mid - 1;
}return ret ? (ret - (x - 1) * block) : 0;
}
inlineint Sec_A(int x, int y, intw) {int ret(0);if(bel[x] ==bel[y]) {for(int i = x; i <= y; i ++) if(A[i] + Add[bel[x]] < w) ret ++;returnret;
}else{for(int i = x; i <= bel[x] * block; i ++) if(A[i] + Add[bel[x]] < w) ret ++;for(int i = (bel[y] - 1) * block + 1; i <= y; i ++)if(A[i] + Add[bel[y]]
ret++;
}for(int i = bel[x] + 1; i < bel[y]; i ++)
ret+=Calc(i, w);returnret;
}intmain() {
n=read();
block=sqrt(n);for(int i = 1; i <= n; i ++) A[i] =read();for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1;if(n % block) cnt = n / block + 1;else cnt = n /block;for(int i = 1; i <= cnt; i ++) Work_sort(i);int T =n;while(T --) {int opt = read(), l = read(), r = read(), c =read();if(!opt) Sec_G(l, r, c);else cout << Sec_A(l, r, c * c) << "\n";
}return 0;
}
数列分块入门 3
区间加法,询问区间内小于某个值 x的前驱
与2类似,二分查找
#include #include#include#include
using namespacestd;const int N = 1e5 + 10;const int oo = 999999999;#define gc getchar()inlineintread() {int x = 0; char c =gc;while(c < '0' || c > '9') c =gc;while(c >= '0' && c <= '9') x = x * 10 + c - '0', c =gc;returnx;
}int A[N], B[N] = {-1}, Add[N], bel[N];intn, block, cnt;void Work_sort(intx) {int l = (x - 1) * block + 1, r = min(l + block - 1, n);for(int i = l; i <= r; i ++) B[i] =A[i];
sort(B+ l, B + r + 1);
}void Sec_G(int x, int y, intw) {if(bel[x] ==bel[y]) {for(int i = x; i <= y; i ++) A[i] +=w;
Work_sort(bel[x]);
}else{for(int i = x; i <= bel[x] * block; i ++) A[i] +=w; Work_sort(bel[x]);for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] +=w; Work_sort(bel[y]);
}for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] +=w;
}
inlineint Calc(int x, intw) {int l = (x - 1) * block + 1, r = min(l + block - 1, n), ret = 0;while(l <=r) {int mid = (l + r) >> 1;if(B[mid] + Add[x] < w) ret = mid, l = mid + 1;else r = mid - 1;
}return B[ret] +Add[x];
}
inlineint Sec_A(int x, int y, intw) {int ret = -1;if(bel[x] ==bel[y]) {for(int i = x; i <= y; i ++) if(A[i] + Add[bel[x]] < w && A[i] + Add[bel[x]] > ret) ret = A[i] +Add[bel[x]];returnret;
}else{for(int i = x; i <= bel[x] * block; i ++)if(A[i] + Add[bel[x]] < w && A[i] + Add[bel[x]] > ret) ret = A[i] +Add[bel[x]];for(int i = (bel[y] - 1) * block + 1; i <= y; i ++)if(A[i] + Add[bel[y]] < w && A[i] + Add[bel[y]] > ret) ret = A[i] +Add[bel[y]];
}for(int i = bel[x] + 1; i < bel[y]; i ++) {int imp =Calc(i, w);if(imp < w && imp > ret) ret =imp;
}returnret;
}intmain() {
n=read();
block=sqrt(n);for(int i = 1; i <= n; i ++) A[i] =read();for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1;if(n % block) cnt = n / block + 1;else cnt = n /block;for(int i = 1; i <= cnt; i ++) Work_sort(i);int T =n;while(T --) {int opt = read(), l = read(), r = read(), c =read();if(!opt) Sec_G(l, r, c);else cout << Sec_A(l, r, c) << "\n";
}return 0;
}
数列分块入门 4
区间加法,区间求和
没什么好说的
#include #include#include
using namespacestd;const int N = 5e4 + 10;#define gc getchar()inlineintread() {int x = 0; char c =gc;while(c < '0' || c > '9') c =gc;while(c >= '0' && c <= '9') x = x * 10 + c - '0', c =gc;returnx;
}#define LL long longLL A[N], Add[N], bel[N], W[N];intn, block, cnt, Mod;void Sec_G(int x, int y, intw) {if(bel[x] == bel[y]) for(int i = x; i <= y; i ++) A[i] += w, W[bel[x]] +=w;else{for(int i = x; i <= bel[x] * block; i ++) A[i] += w, W[bel[x]] +=w;for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w, W[bel[y]] +=w;
}for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w, W[i] += w *block;
}
inlineint Sec_A(int x, inty) {
LL ret= 0;if(bel[x] ==bel[y])for(int i = x; i <= y; i ++)
ret+= (A[i] + Add[bel[x]]) %Mod;else{for(int i = x; i <= bel[x] * block; i ++) ret += (A[i] + Add[bel[x]]) %Mod;for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) ret += (A[i] + Add[bel[y]]) %Mod;
}for(int i = bel[x] + 1; i < bel[y]; i ++) ret += W[i] %Mod;return ret%Mod;
}intmain() {
n=read();
block=sqrt(n);for(int i = 1; i <= n; i ++) A[i] =read();for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1, W[bel[i]] +=A[i];if(n % block) cnt = n / block + 1;else cnt = n /block;int T =n;while(T --) {int opt = read(), l = read(), r = read(), c = read(); Mod = c + 1;if(!opt) Sec_G(l, r, c);else cout << Sec_A(l, r) <
}return 0;
}
数列分块入门 5
区间开方,区间求和
一个数(合理)开几次根后就是0/1了
因此,只需记录每块的最大值,如果最大值是0/1就没必要开根
#include #include#include
using namespacestd;const int N = 5e4 + 10;#define gc getchar()inlineintread() {int x = 0; char c =gc;while(c < '0' || c > '9') c =gc;while(c >= '0' && c <= '9') x = x * 10 + c - '0', c =gc;returnx;
}#define LL long longLL A[N], Add[N], bel[N], W[N], Max[N];intn, block, cnt, Mod;
inlinevoid Sec_G(int x, inty) {if(bel[x] ==bel[y]) {if(!Max[bel[x]]) return;if(Max[bel[x]] == 1) return;for(int i = x; i <= y; i ++) {int C = A[i] - (int) sqrt(A[i]);
W[bel[x]]-= C; A[i] = (int) sqrt(A[i]);
}
LL Max_A= 0;for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) Max_A =max(Max_A, A[i]);
Max[bel[x]]=Max_A;return;
}else{if(Max[bel[x]] && Max[bel[x]] != 1) {for(int i = x; i <= bel[x] * block; i ++) {int C = A[i] - (int) sqrt(A[i]);
W[bel[x]]-=C;
A[i]= (int) sqrt(A[i]);
}
LL Max_A= 0;for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) Max_A =max(Max_A, A[i]);
Max[bel[x]]=Max_A;
}if(Max[bel[y]] && Max[bel[y]] != 1) {for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) {int C = A[i] - (int) sqrt(A[i]);
W[bel[y]]-=C;
A[i]= (int) sqrt(A[i]);
}
LL Max_A= 0;for(int i = (bel[y] - 1) * block + 1; i <= bel[y] * block; i ++) Max_A =max(Max_A, A[i]);
Max[bel[y]]=Max_A;
}
}for(int i = bel[x] + 1; i < bel[y]; i ++) {if(!Max[i] || Max[i] == 1) continue;
LL Max_A= 0;for(int j = (i - 1) * block + 1; j <= i * block; j ++) {int C = A[j] - (int) sqrt(A[j]);
W[i]-=C;
A[j]= (int) sqrt(A[j]);
Max_A=max(Max_A, A[j]);
}
Max[i]=Max_A;
}
}
inlineint Sec_A(int x, inty) {
LL ret= 0;if(bel[x] == bel[y] && Max[bel[x]]) for(int i = x; i <= y; i ++) ret +=A[i];else{for(int i = x; i <= bel[x] * block && Max[bel[x]]; i ++) ret +=A[i];for(int i = (bel[y] - 1) * block + 1; i <= y && Max[bel[y]]; i ++) ret +=A[i];
}for(int i = bel[x] + 1; i < bel[y]; i ++) ret +=W[i];returnret;
}intmain() {
n=read();
block=sqrt(n);for(int i = 1; i <= n; i ++) A[i] =read();for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1, W[bel[i]] += A[i], Max[bel[i]] =max(Max[bel[i]], A[i]);int T =n;while(T --) {int opt = read(), l = read(), r = read(), c =read();if(!opt) Sec_G(l, r);else cout << Sec_A(l, r) << "\n";
}return 0;
}
数列分块入门 6
单点插入,单点询问
数据随机,分块,对于每一块开动态数组,插入 + 查询比较容易实现
如果数据不随机,就有可能加到同一块中的数较多,影响效率
这样可以进行一定的插入操作之后重新分块
#include #include#include#include
using namespacestd;const int N = 1e5 + 10;int A[N << 1], n;
vector Vec[350];intblock, bel[N];#define gc getchar()inlineintread() {int x = 0; char c =gc;while(c < '0' || c > '9') c =gc;while(c >= '0' && c <= '9') x = x * 10 + c - '0', c =gc;returnx;
}
inlinevoid Ins(int x, inta) {int now_size(0), Whi;for(int i = 1; ; i ++) {int Size =Vec[i].size();
now_size+=Size;if(now_size >=x) {
Whi=i;
x-= (now_size -Size);break;
}
}
Vec[Whi].insert(Vec[Whi].begin()+ x - 1, a);
}
inlineint Poi_A(intx) {int Whi_, now_size(0);for(int i = 1; ; i ++) {int Size =Vec[i].size();
now_size+=Size;if(now_size >=x) {int iii = x - (now_size -Size);return Vec[i][iii - 1];
}
}
}intmain() {
n=read();
block=sqrt(n);for(int i = 1; i <= n; i ++) A[i] =read();for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1;for(int i = 1; i <= n; i ++) Vec[bel[i]].push_back(A[i]);int T =n;while(T --) {int opt = read(), l = read(), r = read(), c =read();if(!opt) Ins(l, r);else cout << Poi_A(r) <
}return 0;
}
数列分块入门 7
区间乘法,区间加法,单点询问
先乘后加,乘的时候相应的加法标记也要乘
#include #include#include#include
using namespacestd;const int N = 1e5 + 10;const int Mod = 1e4 + 7;#define LL long longLL A[N], Mul[N], Add[N], bel[N];intn, cnt, block;#define gc getchar()inlineintread() {int x = 0, f = 1; char c =gc;while(c < '0' || c > '9') {if(c == '-') f = -1; c =gc;}while(c >= '0' && c <= '9') x = x * 10 + c - '0', c =gc;returnx;
}
inlinevoid Sec_Add(int x, int y, intw) {if(bel[x] ==bel[y]) {for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[x]] +Add[bel[x]]);
Add[bel[x]]= 0; Mul[bel[x]] = 1;for(int i = x; i <= y; i ++) A[i] += w, A[i] %=Mod;return;
}else{for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) %Mod;
Add[bel[x]]= 0; Mul[bel[x]] = 1;for(int i = x; i <= bel[x] * block; i ++) A[i] += w, A[i] %=Mod;for(int i = (bel[y] - 1) * block + 1; i <= bel[y] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) %Mod;
Add[bel[y]]= 0; Mul[bel[y]] = 1;for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w, A[i] %=Mod;
}for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w, Add[i] %=Mod;
}
inlinevoid Sec_Mul(int x, int y, intw) {if(bel[x] ==bel[y]) {for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) %Mod;
Add[bel[x]]= 0; Mul[bel[x]] = 1;for(int i = x; i <= y; i ++) A[i] = (A[i] * w) %Mod;return;
}else{for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) %Mod;
Add[bel[x]]= 0; Mul[bel[x]] = 1;for(int i = x; i <= bel[x] * block; i ++) A[i] = (A[i] * w) %Mod;for(int i = (bel[y] - 1) * block + 1; i <= bel[y] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) %Mod;
Add[bel[y]]= 0; Mul[bel[y]] = 1;for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] = (A[i] * w) %Mod;
}for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] = (Add[i] * w) % Mod, Mul[i] = (Mul[i] * w) %Mod;
}intmain() {
n=read();for(int i = 1; i <= n; i ++) A[i] =read();
block=sqrt(n);for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1, Mul[i] = 1;int T =n;while(T --) {int opt = read(), l = read(), r = read(), c =read();if(opt == 0) Sec_Add(l, r, c);else if(opt == 1) Sec_Mul(l, r, c);else cout << (A[r] * Mul[bel[r]] + Add[bel[r]]) % Mod << "\n";
}return 0;
}
数列分块入门 8
暴力
区间修改没有什么难度,这题难在区间查询比较奇怪,因为权值种类比较多,似乎没有什么好的维护方法。
模拟一些数据可以发现,询问后一整段都会被修改,几次询问后数列可能只剩下几段不同的区间了。
我们思考这样一个暴力,还是分块,维护每个分块是否只有一种权值,区间操作的时候,对于同权值的一个块就O(1)统计答案,否则暴力统计答案,并修改标记,不完整的块也暴力。
这样看似最差情况每次都会耗费O(n)的时间,但其实可以这样分析:
假设初始序列都是同一个值,那么查询是O(√n),如果这时进行一个区间操作,它最多破坏首尾2个块的标记,所以只能使后面的询问至多多2个块的暴力时间,所以均摊每次操作复杂度还是O(√n)。
换句话说,要想让一个操作耗费O(n)的时间,要先花费√n个操作对数列进行修改。
初始序列不同值,经过类似分析后,就可以放心的暴力啦。
#include #include#include#include#include
using namespacestd;const int N = 1e5 + 10;intbel[N], A[N], bec[N];intn;intblock;#define gc getchar()inlineintread() {int x = 0; char c =gc;while(c < '0' || c > '9') c =gc;while(c >= '0' && c <= '9') x = x * 10 + c - '0', c =gc;returnx;
}int Sec_A(int x, int y, intc) {int ret(0);if(bel[x] ==bel[y]) {if(bec[bel[x]] == c) ret = y - x + 1;else if(bec[bel[x]] == -1) for(int i = x; i <= y; i ++) if(A[i] == c) ret ++;if(~ bec[bel[x]]) for(int i = (bel[x] - 1) * block + 1; i < x; i ++) A[i] =bec[bel[x]];if(~ bec[bel[x]]) for(int i = y + 1; i <= bel[x] * block; i ++) A[i] =bec[bel[x]];for(int i = x; i <= y; i ++) A[i] =c;
bec[bel[x]]= -1;
}else{if(bec[bel[x]] == c) ret += bel[x] * block - x + 1;else if(bec[bel[x]] == -1) for(int i = x; i <= bel[x] * block; i ++) if(A[i] == c) ret ++;if(bec[bel[y]] == c) ret += y - ((bel[y] - 1) *block);else if(bec[bel[y]] == -1) for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) if(A[i] == c) ret ++;for(int i = bel[x] + 1; i < bel[y]; i ++) {if(bec[i] == c) ret +=block;else if(bec[i] == -1)for(int j = (i - 1) * block + 1; j <= i * block; j ++)if(A[j] == c) ret ++;
}if(~ bec[bel[x]]) for(int i = (bel[x] - 1) * block + 1; i < x; i ++) A[i] =bec[bel[i]];for(int i = x; i <= bel[x] * block; i ++) A[i] =c;
bec[bel[x]]= -1;for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] =c;if(~ bec[bel[y]]) for(int i = y + 1; i <= bel[y] * block; i ++) A[i] =bec[bel[i]];
bec[bel[y]]= -1;for(int i = bel[x] + 1; i < bel[y]; i ++) bec[i] =c;
}returnret;
}intmain() {
n=read();for(int i = 1; i <= n; i ++) bec[i] = -1;for(int i = 1; i <= n; i ++) A[i] =read();
block=sqrt(n);for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1;int T =n;while(T --) {int l = read(), r = read(), c =read();
cout<< Sec_A(l, r, c) << "\n";
}return 0;
}
数列分块入门 9
区间众数查询
陈立杰区间众数解题报告
#include#include#include#include#include#include#include#include#include#include#include
#define mod 10007
#define pi acos(-1)
#define inf 0x7fffffff
#define ll long long
using namespacestd;
ll read() {
ll x=0,f=1;char ch=getchar();while(ch'9') {if(ch=='-')f=-1;
ch=getchar();
}while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}return x*f;
}intn,blo,id;int v[50005],bl[50005];int f[505][505];
mapmp;int val[50005],cnt[50005];
vectorve[50005];void pre(intx) {
memset(cnt,0,sizeof(cnt));int mx=0,ans=0;for(int i=(x-1)*blo+1; i<=n; i++) {
cnt[v[i]]++;int t=bl[i];if(cnt[v[i]]>mx||(cnt[v[i]]==mx&&val[v[i]]
ans=v[i],mx=cnt[v[i]];
f[x][t]=ans;
}
}int query(int l,int r,intx) {int t=upper_bound(ve[x].begin(),ve[x].end(),r)-lower_bound(ve[x].begin(),ve[x].end(),l);returnt;
}int query(int a,intb) {intans,mx;
ans=f[bl[a]+1][bl[b]-1];
mx=query(a,b,ans);for(int i=a; i<=min(bl[a]*blo,b); i++) {int t=query(a,b,v[i]);if(t>mx||(t==mx&&val[v[i]]
}if(bl[a]!=bl[b])for(int i=(bl[b]-1)*blo+1; i<=b; i++) {int t=query(a,b,v[i]);if(t>mx||(t==mx&&val[v[i]]
}returnans;
}intmain() {
n=read();
blo=200;for(int i=1; i<=n; i++) {
v[i]=read();if(!mp[v[i]]) {
mp[v[i]]=++id;
val[id]=v[i];
}
v[i]=mp[v[i]];
ve[v[i]].push_back(i);
}for(int i=1; i<=n; i++)bl[i]=(i-1)/blo+1;for(int i=1; i<=bl[n]; i++)pre(i);for(int i=1; i<=n; i++) {int a=read(),b=read();if(a>b)swap(a,b);
printf("%d\n",val[query(a,b)]);
}return 0;
}
分块算法小结:
暴力算法
时间复杂度可以
空间允许
优美