分块入门(根据hzwer的博客。。)(右端点是r不是n。。)
1.区间更新单点查询
#include <bits/stdc++.h> using namespace std; #define ll long long #define maxn 100005 int n, len, pos[maxn], a[maxn]; struct Block { int add; } b[1000]; void update(int l, int r, int c) { int p = pos[l], q = pos[r]; if (p == q) for (int i = l; i <= r; i++) a[i] += c; else { for (int i = p + 1; i <= q - 1; i++) b[i].add += c; for (int i = l; i <= p * len; i++) a[i] += c; for (int i = (q - 1) * len + 1; i <= r; i++) a[i] += c; } } int main() { cin >> n; len = sqrt(n); for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) pos[i] = (i - 1) / len + 1; int opt, l, r, c; for (int i = 1; i <= n; i++) { scanf("%d%d%d%d", &opt, &l, &r, &c); if (opt == 0) update(l, r, c); else cout << a[r] + b[pos[r]].add << '\n'; } }
2.区间求小于c的数的个数:先预处理,即把每块的元素sort一下,整块修改用add标记,查询用lower_bound,两端修改后用sort维护,查询暴力统计即可
注意要把add当做块的属性,而不要下放到块中的元素里去
/* 预处理块时需要将块内元素进行排序,然后可以每次查询都是用二分来做 区间更新时要将两端元素进行单独修改+排序 */ #include <bits/stdc++.h> using namespace std; #define ll long long #define maxn 50005 struct Block { int add; vector<int> v; } b[5005]; int n, pos[maxn], len; int a[maxn]; inline void calc(int p) { b[p].v.clear(); for (int i = (p - 1) * len + 1; i <= min(p * len, n); i++) b[p].v.push_back(a[i]); sort(b[p].v.begin(), b[p].v.end()); } void update(int l, int r, int c) { int p = pos[l], q = pos[r]; if (p == q) { for (int i = l; i <= r; i++) a[i] += c; calc(p); return; } for (int i = p + 1; i <= q - 1; i++) b[i].add += c; for (int i = l; i <= min(p * len, n); i++) a[i] += c; for (int i = (q - 1) * len + 1; i <= r; i++) a[i] += c; calc(p); calc(q); } int query(int l, int r, int c) { int res = 0; int p = pos[l], q = pos[r]; if (p == q) { for (int i = l; i <= r; i++) if (a[i] + b[p].add < c) res++; return res; } for (int i = p + 1; i <= q - 1; i++) { int x = c - b[i].add; int tmp = lower_bound(b[i].v.begin(), b[i].v.end(), x) - b[i].v.begin(); res += tmp; } for (int i = l; i <= min(p * len, n); i++) if (a[i] + b[p].add < c) res++; for (int i = (q - 1) * len + 1; i <= r; i++) if (a[i] + b[q].add < c) res++; return res; } int main() { cin >> n; len = sqrt(n); for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) { pos[i] = (i - 1) / len + 1; b[pos[i]].v.push_back(a[i]); } for (int i = 1; i <= pos[n]; i++) //预处理先排序 sort(b[i].v.begin(), b[i].v.end()); int m = n; while (m--) { ll opt, l, r, c; scanf("%d%d%d%d", &opt, &l, &r, &c); if (opt == 0) update(l, r, c); else cout << query(l, r, c * c) << endl; } }
3.区间更新+找区间内小于c的最大的数(c的前驱)
块设为sqrt(n)就会t,设为1000就没事。。玄学复杂度啊
/* set每次修改都是logn,那么n次块内更新耗时n*sqrt(n)*logsqrt(n) */ #include <bits/stdc++.h> using namespace std; #define maxn 100005 struct Block { int add; set<int> s; } b[1005]; int n, pos[maxn], len, a[maxn]; set<int>::iterator it; void update(int l, int r, int c) { int p = pos[l], q = pos[r]; if (p == q) { for (int i = l; i <= r; i++) { b[p].s.erase(a[i]); a[i] += c; b[p].s.insert(a[i]); } return; } for (int i = p + 1; i <= q - 1; i++) b[i].add += c; for (int i = l; i <= min(p * len, n); i++) { b[p].s.erase(a[i]); a[i] += c; b[p].s.insert(a[i]); } for (int i = (q - 1) * len + 1; i <= r; i++) { b[q].s.erase(a[i]); a[i] += c; b[q].s.insert(a[i]); } } int query(int l, int r, int c) { int res = -1, p = pos[l], q = pos[r]; if (p == q) { for (int i = l; i <= r; i++) { int x = a[i] + b[p].add; if (x < c) res = max(res, x); } return res; } for (int i = p + 1; i <= q - 1; i++) { //大块里用set查 int x = c - b[i].add; it = b[i].s.lower_bound(x); if (it == b[i].s.begin()) continue; it--; res = max(res, (*it) + b[i].add); } for (int i = l; i <= min(p * len, n); i++) if (a[i] + b[p].add < c) res = max(res, a[i] + b[p].add); for (int i = (q - 1) * len + 1; i <= r; i++) if (a[i] + b[q].add < c) res = max(res, a[i] + b[q].add); return res; } int main() { cin >> n; len = 1500; for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= n; i++) { pos[i] = (i - 1) / len + 1; b[pos[i]].s.insert(a[i]); } for (int i = 1; i <= n; i++) { int opt, l, r, c; scanf("%d%d%d%d", &opt, &l, &r, &c); if (opt == 0) update(l, r, c); else cout << query(l, r, c) << endl; } }
4.区间更新,区间求和
#include <bits/stdc++.h> using namespace std; #define ll long long #define maxn 50005 struct Block { ll add, sum; } b[500]; int n, pos[maxn], len; ll a[maxn]; void update(int l, int r, int c) { int p = pos[l], q = pos[r]; if (p == q) { for (int i = l; i <= r; i++) a[i] += c, b[p].sum += c; return; } for (int i = p + 1; i <= q - 1; i++) b[i].add += c; for (int i = l; i <= min(p * len, n); i++) a[i] += c, b[p].sum += c; for (int i = (q - 1) * len + 1; i <= r; i++) a[i] += c, b[q].sum += c; } ll query(int l, int r, int c) { ll res = 0; int p = pos[l], q = pos[r]; if (p == q) { for (int i = l; i <= r; i++) res += a[i] + b[p].add; return res % c; } for (int i = p + 1; i <= q - 1; i++) res += b[i].sum, res += len * b[i].add; for (int i = l; i <= min(p * len, n); i++) res += b[p].add, res += a[i]; for (int i = (q - 1) * len + 1; i <= r; i++) res += b[q].add, res += a[i]; return res % c; } int main() { cin >> n; len = sqrt(n); for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) { pos[i] = (i - 1) / len + 1; b[pos[i]].sum += a[i]; } for (int i = 1; i <= n; i++) { int opt, l, r, c; scanf("%d%d%d%d", &opt, &l, &r, &c); if (opt == 0) update(l, r, c); else cout << query(l, r, c + 1) << endl; } }
5.区间开根(类似于势能线段树)
/* 2*2=4,4*4=16,16*16=256,每个数最多被开4次,开了4次以上就是1 所以两端暴力开,整块判断块中所有元素是不是1,是的话那么这个块就不用开了 修改最多4n,查询就是n*根号n */ #include <bits/stdc++.h> using namespace std; #define maxn 50005 struct Block { int flag; int sum; } b[500]; int n, len, pos[maxn], a[maxn]; void calc(int p) { if (b[p].flag) return; b[p].flag = 1; b[p].sum = 0; for (int i = (p - 1) * len + 1; i <= p * len; i++) { a[i] = (int)sqrt(a[i]); b[p].sum += a[i]; if (a[i] > 1) b[p].flag = 0; } } void update(int l, int r) { int p = pos[l], q = pos[r]; if (p == q) { for (int i = l; i <= r; i++) { b[p].sum -= a[i]; a[i] = sqrt(a[i]); b[p].sum += a[i]; } return; } for (int i = p + 1; i <= q - 1; i++) calc(i); for (int i = l; i <= min(r, p * len); i++) { b[p].sum -= a[i]; a[i] = sqrt(a[i]); b[p].sum += a[i]; } for (int i = (q - 1) * len + 1; i <= r; i++) { b[q].sum -= a[i]; a[i] = sqrt(a[i]); b[q].sum += a[i]; } } int query(int l, int r) { int res = 0, p = pos[l], q = pos[r]; if (p == q) { for (int i = l; i <= r; i++) res += a[i]; return res; } for (int i = p + 1; i <= q - 1; i++) res += b[i].sum; for (int i = l; i <= min(p * len, r); i++) res += a[i]; for (int i = (q - 1) * len + 1; i <= r; i++) res += a[i]; return res; } int main() { cin >> n; len = sqrt(n); for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) { pos[i] = (i - 1) / len + 1; b[pos[i]].sum += a[i]; } for (int i = 1; i <= n; i++) { int opt, l, r, c; cin >> opt >> l >> r >> c; if (opt == 0) update(l, r); else cout << query(l, r) << endl; } }
6.块状数组,块状链表:用来解决在数列中插入一个数
/* 块内维护一个数组和size 再用前缀和log根号n 的时间内找到新来的数应该加在哪个块中 然后每增加一个数都要消耗该数所在块的长度的时间 如果数据随机,那么一次插入就是O(根号n) 如果不随机。。可能n个数都加在同一个块内,那么会导致复杂度退化为n^2 那么当块过大时进行重构即可,这里将块大小上限设置为20*根号n */ #include<bits/stdc++.h> #include<vector> using namespace std; #define maxn 200005 struct Block{ vector<int>v; }b[20005]; int nn,n,len,m,pos[maxn],a[maxn];//块的长度,块的个数 void rebuild(){ nn=0; for(int i=1;i<=m;i++){ for(int j=0;j<b[i].v.size();j++) a[++nn]=b[i].v[j]; b[i].v.clear(); } len=sqrt(nn);m=(nn-1)/len+1; for(int i=1;i<=nn;i++){ pos[i]=(i-1)/len+1; b[pos[i]].v.push_back(a[i]); } } void update(int pos,int x){//在位置pos插入x int p=1; while(b[p].v.size()<pos) pos-=b[p].v.size(),p++; b[p].v.insert(b[p].v.begin()+pos-1,x); if(b[p].v.size()>20*len) rebuild(); } int query(int pos){ int p=1; while(b[p].v.size()<pos) pos-=b[p].v.size(),p++; return b[p].v[pos-1]; } int main(){ cin>>n;len=sqrt(n); for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++){ pos[i]=(i-1)/len+1; b[pos[i]].v.push_back(a[i]); } m=pos[n]; for(int i=1;i<=n;i++){ int opt,l,r,c; cin>>opt>>l>>r>>c; if(opt==0)update(l,r); else cout<<query(r)<<endl; } }
7.标记下传顺序(标记优先级)
/* 把数字表示成a*mul+add的形式 */ #include<bits/stdc++.h> using namespace std; #define maxn 100005 #define mod 10007 #define ll long long struct Block{ int mul,add; }b[1005]; int a[maxn],n,len,pos[maxn]; void pushdown(int p){ for(int i=(p-1)*len+1;i<=min(p*len,n);i++) a[i]=((ll)a[i]*b[p].mul+b[p].add)%mod; b[p].add=0;b[p].mul=1; }/* void add(int l,int r,int c){ int p=pos[l],q=pos[r]; if(p==q){ pushdown(p); for(int i=l;i<=r;i++) a[i]=(a[i]+c)%mod; return; } for(int i=p+1;i<=q-1;i++) b[i].add=(b[i].add+(ll)b[i].mul*c)%mod; pushdown(p);pushdown(q); for(int i=l;i<=min(p*len,r);i++) a[i]=(a[i]+c)%mod; for(int i=(q-1)*len+1;i<=r;i++) a[i]=(a[i]+c)%mod; } void mul(int l,int r,int c){ int p=pos[l],q=pos[r]; if(p==q){ pushdown(p); for(int i=l;i<=r;i++) a[i]=((ll)a[i]*c)%mod; return; } for(int i=p+1;i<=q-1;i++){ b[i].mul=((ll)b[i].mul*c)%mod; b[i].add=((ll)b[i].add*c)%mod; } pushdown(p);pushdown(q); for(int i=1;i<=min(p*len,r);i++) a[i]=((ll)a[i]*c)%mod; for(int i=(q-1)*len+1;i<=r;i++) a[i]=((ll)a[i]*c)%mod; }*/ void update(int f,int l,int r,int c){ int p=pos[l],q=pos[r]; pushdown(p); for(int i=l;i<=min(r,p*len);i++){ if(f==0)a[i]+=c; else a[i]*=c; a[i]%=mod; } if(p!=q){ pushdown(q); for(int i=(q-1)*len+1;i<=r;i++){ if(f==0)a[i]+=c; else a[i]*=c; a[i]%=mod; } } for(int i=p+1;i<=q-1;i++){ if(f==0) b[i].add=(b[i].add+c)%mod; else { b[i].mul=(b[i].mul*c)%mod; b[i].add=(b[i].add*c)%mod; } } } int query(int x){ int p=pos[x]; return ((ll)a[x]*b[p].mul+b[p].add)%mod; } int main(){ cin>>n;len=sqrt(n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++) pos[i]=(i-1)/len+1; for(int i=1;i<=pos[n];i++) b[i].mul=1; for(int i=1;i<=n;i++){ int opt,l,r,c; scanf("%d%d%d%d",&opt,&l,&r,&c); if(opt==2)cout<<query(r)<<endl; else update(opt,l,r,c); } }
8.区间覆盖
/* 区间众数 覆盖标记 */ #include<bits/stdc++.h> using namespace std; #define maxn 100005 struct Block{ int c; }b[500]; int a[maxn],n,pos[maxn],len; void pushdown(int p){ if(b[p].c!=-1){ for(int i=(p-1)*len+1;i<=p*len;i++) a[i]=b[p].c; b[p].c=-1; } } int query(int l,int r,int c){ int res=0,p=pos[l],q=pos[r]; if(p==q){ pushdown(p); for(int i=l;i<=r;i++) if(a[i]==c)res++; else a[i]=c; return res; } pushdown(p);pushdown(q); for(int i=l;i<=min(p*len,r);i++) if(a[i]==c)res++; else a[i]=c; for(int i=(q-1)*len+1;i<=r;i++) if(a[i]==c)res++; else a[i]=c; for(int i=p+1;i<=q-1;i++) if(b[i].c!=-1){ if(b[i].c==c)res+=len; else b[i].c=c; } else { for(int j=(i-1)*len+1;j<=i*len;j++) if(a[j]==c)res++; else a[j]=c; b[i].c=c; } return res; } int main(){ cin>>n;len=sqrt(n); for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++) pos[i]=(i-1)/len+1,b[pos[i]].c=-1; for(int i=1;i<=n;i++){ int l,r,c; cin>>l>>r>>c; cout<<query(l,r,c)<<endl; } }