Loj#6278. 数列分块入门 2
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值c^2的元素个数。
样例输入
4
1 2 2 3
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2
样例输出
3 0 2
分析:复习了一下分块.
对于修改操作,在整块外的直接暴力修改,否则给每个块维护一个修改标记. 这样每个元素的最终值就是它本身 + 它所属块的修改标记.
对于查询操作,整块外的暴力统计,整块内的需要给每一块排序,二分即可.
几个易错点:
1.不要直接排序原序列.
2.注意序列的下标和块的下标不要弄混了.
3.如果修改的区间在一个整块内,则直接暴力修改.
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll maxn = 50010; ll n,block,pos[maxn],l[maxn],r[maxn],cnt,a[maxn],add[maxn]; vector <ll> b[maxn]; void Sort(int x) { b[x].clear(); for (int i = l[x]; i <= r[x]; i++) b[x].push_back(a[i]); sort(b[x].begin(),b[x].end()); } void update(ll L,ll R,ll x) { if (pos[L] == pos[R]) { for (ll i = L; i <= R; i++) a[i] += x; Sort(pos[L]); return; } for (ll i = pos[L] + 1; i <= pos[R] - 1; i++) add[i] += x; for (ll i = L; i <= r[pos[L]]; i++) a[i] += x; for (ll i = l[pos[R]]; i <= R; i++) a[i] += x; Sort(pos[L]); Sort(pos[R]); } ll query(ll L,ll R,ll x) { ll res = 0; if (pos[L] == pos[R]) { for (ll i = L; i <= R; i++) if (a[i] + add[pos[i]] < x) res++; return res; } for (ll i = L; i <= r[pos[L]]; i++) if (a[i] + add[pos[i]] < x) res++; for (ll i = l[pos[R]]; i <= R; i++) if (a[i] + add[pos[i]]< x) res++; for (ll i = pos[L] + 1; i <= pos[R] - 1; i++) { int temp = x - add[i]; res += lower_bound(b[i].begin(),b[i].end(),temp) - b[i].begin(); } return res; } int main() { freopen("test.txt","r",stdin); scanf("%lld",&n); block = sqrt(n); for (ll i = 1; i <= n; i++) { scanf("%lld",&a[i]); pos[i] = (i - 1) / block + 1; b[pos[i]].push_back(a[i]); } cnt = (n - 1) / block + 1; for (ll i = 1; i <= cnt; i++) { l[i] = r[i - 1] + 1; r[i] = min(i * block,n); sort(b[i].begin(),b[i].end()); } for (ll i = 1; i <= n; i++) { ll opt,L,R,c; scanf("%lld%lld%lld%lld",&opt,&L,&R,&c); if (opt == 0) update(L,R,c); else printf("%lld\n",query(L,R,c*c)); } return 0; }